Compare commits

...

1030 Commits

Author SHA1 Message Date
Abdullah Atta
dd209354d3 web: show error if saving a note fails 2024-02-19 18:00:57 +05:00
Abdullah Atta
193d99a9ec core: fix database image is malformed error on updating locked note 2024-02-19 18:00:27 +05:00
Abdullah Atta
61e83a999c web: bump version to 3.0.3-beta 2024-02-19 17:04:06 +05:00
Abdullah Atta
ec846ece9f desktop: include preload script when comparing bundles 2024-02-19 16:36:17 +05:00
Abdullah Atta
0660f5acf6 core: add more lookup tests 2024-02-19 16:35:39 +05:00
Abdullah Atta
786d237005 core: allow testing with persisted database 2024-02-19 16:35:27 +05:00
Abdullah Atta
a9698916dd core: return items in same order when using .records 2024-02-19 16:35:06 +05:00
Abdullah Atta
03380ca9de core: improve notes lookup performance 2024-02-19 16:34:39 +05:00
Abdullah Atta
7e886b787e core: refactor test setup & use better-sqlite-multiple-cipher 2024-02-19 16:34:03 +05:00
Abdullah Atta
55eaebe8a1 web: destroy sql database on app close 2024-02-19 16:13:32 +05:00
Abdullah Atta
78248bf8b3 core: fix database image is malformed error 2024-02-19 16:12:45 +05:00
Abdullah Atta
1a73d9a888 core: fix note headling getting removed 2024-02-18 10:08:49 +05:00
Abdullah Atta
ea35136227 web: fix notebook indentation 2024-02-17 21:45:53 +05:00
Abdullah Atta
ce15ec79fd core: fix sorting of newly created notes 2024-02-17 21:30:31 +05:00
Abdullah Atta
a1dfee8319 web: allow sorting by title everywhere 2024-02-17 11:24:47 +05:00
Abdullah Atta
e4a030c5c9 core: add support for sorting by title & date together 2024-02-17 11:24:25 +05:00
Abdullah Atta
020c5c8f0c web: add child notebook under opened notebook 2024-02-16 17:35:05 +05:00
Abdullah Atta
a546354421 web: rerender subnotebooks on root notebook change 2024-02-16 17:34:26 +05:00
Abdullah Atta
eb55d0d7c9 web: hide sort button in notebook view 2024-02-16 17:33:51 +05:00
Abdullah Atta
1e7c18fff4 web: await notes context refreshing 2024-02-16 17:33:10 +05:00
Abdullah Atta
62861d1ee6 web: clear keystore when clearing database 2024-02-16 17:32:46 +05:00
Abdullah Atta
32ba0ac2d9 web: allow clearing keystore 2024-02-16 17:32:27 +05:00
Abdullah Atta
23702e4d1e web: fix empty root notebook indentation 2024-02-16 17:32:07 +05:00
Abdullah Atta
da14a77105 web: fix monograph publish status not updating 2024-02-16 17:31:49 +05:00
Abdullah Atta
b127ce17ba web: fix nested notebook navigation from shortcuts 2024-02-16 17:31:20 +05:00
Abdullah Atta
44ac3732a7 web: fix color cannot be renamed 2024-02-16 15:29:31 +05:00
Abdullah Atta
ed1c963e6a web: allow non-pro users to customize sidebar 2024-02-16 15:27:13 +05:00
Abdullah Atta
6cc530c452 web: reimplement app lock & web key store 2024-02-16 15:12:48 +05:00
Ammar Ahmed
44c38a8598 mobile: fix app lock on ios 2024-02-15 19:33:57 +05:00
Ammar Ahmed
93d55abb1a mobile: v3.0.4 2024-02-15 17:27:33 +05:00
Ammar Ahmed
fe02385b52 editor: fix color picker missing prop 2024-02-15 16:28:36 +05:00
Ammar Ahmed
20f322a98d mobile: improve verification flow before changing app lock 2024-02-15 16:07:33 +05:00
Ammar Ahmed
f8c8a2b913 mobile: fix reordering in side menu 2024-02-15 16:05:56 +05:00
Ammar Ahmed
ee5ebfe7eb editor: fix keyboard focus issues on mobile 2024-02-15 16:04:03 +05:00
Ammar Ahmed
a04a59ec69 mobile: fix bugs 2024-02-15 10:31:00 +05:00
Ammar Ahmed
7873251d05 mobile: fix sidemenu shortcuts 2024-02-15 10:31:00 +05:00
Abdullah Atta
a409eb3a0f web: remove security key config on app lock disable 2024-02-14 15:57:41 +05:00
Abdullah Atta
861d846720 core: fix object id date parsing 2024-02-14 14:07:14 +05:00
Abdullah Atta
74f52e77ab web: fix cr.isIndexedDBSupported is not a function 2024-02-14 14:02:00 +05:00
Ammar Ahmed
4f09e8bb38 mobile: v3.0.3 beta 2024-02-14 12:42:55 +05:00
Ammar Ahmed
8aad4358fb mobile: fix backup failed on iOS 2024-02-14 10:27:58 +05:00
Ammar Ahmed
cd0a5ae173 mobile: bump version 2024-02-13 12:10:33 +05:00
Ammar Ahmed
2677ec4744 mobile: fix issue reporting 2024-02-13 12:09:19 +05:00
Ammar Ahmed
3202f46ffb mobile: fix rendering slow in list 2024-02-13 11:41:44 +05:00
Abdullah Atta
900f82a991 ui: fix right clicking when menu is open doesn't close it 2024-02-13 11:19:03 +05:00
Abdullah Atta
13bbe2149e ui: fix hover highlighting 2 items in menu 2024-02-13 11:18:47 +05:00
Abdullah Atta
603593a312 web: fix color renaming and add color remove support 2024-02-13 11:18:29 +05:00
Abdullah Atta
0bd385395e web: add readonly indicator on notes 2024-02-13 11:18:05 +05:00
Abdullah Atta
af1312977a web: fix compact view not affecting notes 2024-02-13 11:17:53 +05:00
Abdullah Atta
5002947c27 editor: fix Position -1 is outside of fragment error 2024-02-13 10:46:17 +05:00
Ammar Ahmed
3ed5a031a4 mobile: fix migration bugs 2024-02-12 19:22:12 +05:00
Abdullah Atta
5ba87df099 core: minor refactoring 2024-02-12 15:19:51 +05:00
Abdullah Atta
a1dd4e7a98 core: migrate deleted items as well 2024-02-12 13:50:21 +05:00
Abdullah Atta
21d429f764 web: refresh app on sync completed 2024-02-12 13:06:20 +05:00
Abdullah Atta
f6fba20baf core: fix continuous invalid_grant error if login fails 2024-02-12 12:45:16 +05:00
Abdullah Atta
7542204c6e web: fix app hangs when downloading logs 2024-02-12 12:44:34 +05:00
Abdullah Atta
c5527e3893 web: bump to v3.0.2-beta 2024-02-12 11:49:30 +05:00
Abdullah Atta
0b221cd900 web: hide window/tab title on app lock 2024-02-12 11:48:01 +05:00
Abdullah Atta
d6830c5488 core: add support for creating backups of legacy collections 2024-02-12 11:38:35 +05:00
Abdullah Atta
a7ab07d570 web: fix P.item is undefined error 2024-02-11 13:18:10 +05:00
Abdullah Atta
fec1370239 core: remove deleteReason property 2024-02-11 09:51:11 +05:00
Ammar Ahmed
75704a08ea Update ios.publish.yml
Signed-off-by: Ammar Ahmed <40239442+ammarahm-ed@users.noreply.github.com>
2024-02-11 00:39:58 +05:00
Ammar Ahmed
081b4d4eb1 mobile: cleanup gradle.properties 2024-02-11 00:15:06 +05:00
Ammar Ahmed
e1e850f493 mobile: fix share extension 2024-02-11 00:02:00 +05:00
Ammar Ahmed
8e34679fd5 mobile: beta release 2024-02-10 22:36:29 +05:00
Ammar Ahmed
c2200ee8e1 mobile: fix notebook move to trash 2024-02-10 22:34:13 +05:00
Ammar Ahmed
4c86bac8e0 mobile: cleanup 2024-02-10 22:34:13 +05:00
Abdullah Atta
b152fa7463 desktop: disable downgrades 2024-02-10 22:16:39 +05:00
Ammar Ahmed
949f6ace54 mobile: stop app updates check in beta 2024-02-10 21:23:02 +05:00
Ammar Ahmed
5c6fd953eb mobile: minor fixes 2024-02-10 21:16:26 +05:00
Abdullah Atta
0326c3fee9 web: improve database deletion 2024-02-10 19:52:54 +05:00
Abdullah Atta
d7967d322e web: reload page on reset from error boundary 2024-02-10 19:52:54 +05:00
Abdullah Atta
62d2a4471d web: locked status not updating on note 2024-02-10 19:52:54 +05:00
Abdullah Atta
42df6536c0 web: only save note's content through vault 2024-02-10 19:52:54 +05:00
Abdullah Atta
e9d9252182 core: fix adding note to vault should return its id 2024-02-10 19:52:53 +05:00
Abdullah Atta
a400350e48 core: fix database resetting 2024-02-10 19:52:53 +05:00
Abdullah Atta
1dcbf93b3a theme: fix error input variant 2024-02-10 19:52:53 +05:00
Abdullah Atta
620c0b5006 web: allow typing custom date when adding reminder 2024-02-10 19:52:53 +05:00
Abdullah Atta
d3468401f9 common: use upcoming reminder when resolving reminders on note 2024-02-10 19:52:53 +05:00
Ammar Ahmed
a6b83f503e mobile: fix reminders 2024-02-10 19:06:16 +05:00
Ammar Ahmed
e52ad6186f mobile: fix notebooks & colors 2024-02-10 18:56:45 +05:00
Abdullah Atta
59486a7894 core: set mode to "sync" for migrations during sync 2024-02-10 13:42:43 +05:00
Abdullah Atta
76fbfd3251 desktop: patch better-sqlite3-multiple-ciphers to get prebuilt binaries 2024-02-10 13:30:45 +05:00
Abdullah Atta
804dff4a53 web: bump to 3.0.1-beta 2024-02-10 11:11:45 +05:00
Abdullah Atta
a1df8fbf21 core: enable sync during tests 2024-02-10 11:08:48 +05:00
Abdullah Atta
24c69a8c73 core: improve backup importing 2024-02-10 11:00:51 +05:00
Abdullah Atta
16e483954e core: add support for nested transactions 2024-02-10 11:00:51 +05:00
Abdullah Atta
7315192e1a web: use single transaction for importing a backup 2024-02-10 11:00:51 +05:00
Abdullah Atta
f0d3cca188 web: make sqlite slightly faster 2024-02-10 11:00:51 +05:00
Ammar Ahmed
557baf43a3 mobile: fix release build 2024-02-09 12:24:46 +05:00
Abdullah Atta
b9c8ac091f core: fix type errors 2024-02-09 11:06:11 +05:00
Ammar Ahmed
5add1b1d48 mobile: minor fixes 2024-02-09 10:41:21 +05:00
Ammar Ahmed
2734e50a97 core: fix migration bugs 2024-02-09 10:41:00 +05:00
Ammar Ahmed
ab7814c0cb mobile: fix saving user key 2024-02-07 23:04:27 +05:00
Abdullah Atta
f07269f4ea web: fix monograph publishing popup 2024-02-07 19:52:15 +05:00
Abdullah Atta
131ed88906 web: ui fixes 2024-02-07 19:35:50 +05:00
Abdullah Atta
d03b46d2c1 web: fix app lock not getting disabled 2024-02-07 19:35:49 +05:00
Ammar Ahmed
848c25adeb mobile: minor fixes 2024-02-07 19:18:10 +05:00
Ammar Ahmed
61f5153c9f mobile: locked notes 2024-02-07 18:52:35 +05:00
Abdullah Atta
86b6c71501 web: fix indexedDB not getting detected 2024-02-07 15:59:48 +05:00
Abdullah Atta
fa11312c46 core: destroy kysely on error 2024-02-07 13:40:17 +05:00
Abdullah Atta
e032852632 crypto: move libsodium & sodium-native to peer deps 2024-02-07 13:40:17 +05:00
Abdullah Atta
9a4ebb5662 global: pin node version to 20 2024-02-07 13:40:17 +05:00
Abdullah Atta
7631612935 web: various fixes 2024-02-07 13:40:17 +05:00
Abdullah Atta
d5ce877d11 common: fix note resolution 2024-02-07 13:40:17 +05:00
Abdullah Atta
446005b8e2 core: fix database migration 2024-02-07 13:40:17 +05:00
Abdullah Atta
dd7765df97 core: disable sync push 2024-02-07 13:40:17 +05:00
Abdullah Atta
b32b3a5af3 web: trigger error boundary on unhandled errors 2024-02-07 13:40:16 +05:00
Abdullah Atta
e3cc211355 web: use safeStorage to encrypt secrets on desktop 2024-02-07 13:40:16 +05:00
Abdullah Atta
41fed6e347 desktop: use better-sqlite3 on desktop for SQLite 2024-02-07 13:40:16 +05:00
Abdullah Atta
8766966c27 desktop: fix autoupdater 2024-02-07 13:40:16 +05:00
Abdullah Atta
a2972ea9e6 desktop: add api to delete files 2024-02-07 13:40:16 +05:00
Abdullah Atta
99a3f9e779 desktop: expose electron's safeStorage api via trpc 2024-02-07 13:40:16 +05:00
Abdullah Atta
33aad3ec3f desktop: enable native modules rebuild during build 2024-02-07 13:40:16 +05:00
Ammar Ahmed
92c6e9c0bb core: return decrypted content on unlock note 2024-02-07 13:31:53 +05:00
Ammar Ahmed
7a0ec7ca11 mobile: applock with pin and password 2024-02-07 13:03:25 +05:00
Ammar Ahmed
fd1e39cee4 mobile: minor fixes 2024-02-07 11:00:24 +05:00
Ammar Ahmed
bc0f92ea5d mobile: sqlite-multiple-ciphers patch 2024-02-06 23:15:40 +05:00
Ammar Ahmed
2b2a9deb71 mobile: refactor 2024-02-06 12:08:41 +05:00
Ammar Ahmed
b2ac5965d4 mobile: fix reorderable side menu 2024-02-06 00:00:14 +05:00
Ammar Ahmed
7397d5bd01 mobile: yearly reminders 2024-02-05 23:00:46 +05:00
Abdullah Atta
7bca839f6d web: add support for yearly reminders 2024-02-05 22:58:50 +05:00
Abdullah Atta
017b466dc0 core: add support for yearly reminders 2024-02-05 22:57:43 +05:00
Ammar Ahmed
b4f7a7112b mobile: stop bg fetch events when toggled off 2024-02-05 22:55:50 +05:00
Ammar Ahmed
47c6b80594 mobile: enable experimental background sync 2024-02-05 22:55:19 +05:00
Abdullah Atta
4e28793c0e web: add support for hiding items from side bar 2024-02-05 22:50:50 +05:00
Abdullah Atta
24f1307955 web: add support for reordering navigation menu items 2024-02-05 22:50:45 +05:00
Ammar Ahmed
56a9b5a5d9 mobile: cleanup 2024-02-05 22:47:54 +05:00
Ammar Ahmed
884fbf65e9 core: add sidebar order settings 2024-02-05 22:47:54 +05:00
Ammar Ahmed
bc03ad8339 mobile: reorder sidebar items 2024-02-05 22:47:43 +05:00
Abdullah Atta
2fca6b2431 web: add support for custom colors 2024-02-05 22:39:23 +05:00
Ammar Ahmed
4067a7f7c7 mobile: allow custom colors 2024-02-05 22:38:55 +05:00
Ammar Ahmed
a9179734d0 mobile: migrate app lock 2024-02-05 22:34:46 +05:00
Abdullah Atta
0f2f2f085b web: remove cbor-x package 2024-02-05 22:31:37 +05:00
Abdullah Atta
b9e04b763f web: add support for at rest encryption and app lock 2024-02-05 22:30:46 +05:00
Ammar Ahmed
eb23224687 mobile: at rest encryption 2024-02-05 22:28:51 +05:00
Ammar Ahmed
70f39fdc2d mobile: simplify tests 2024-02-05 22:21:14 +05:00
Abdullah Atta
3eea4f73e4 core: remove dummy test files 2024-02-05 22:21:14 +05:00
Abdullah Atta
226b9f5892 core: migrate key value storage to sqlite 2024-02-05 22:21:14 +05:00
Ammar Ahmed
47ef642648 mobile: update sheet component 2024-02-05 22:21:14 +05:00
Abdullah Atta
35b7ef9204 core: fix moving nested notebooks to trash 2024-02-05 22:21:14 +05:00
Abdullah Atta
cf024eefd8 global: move item resolution logic to @notesnook/common 2024-02-05 22:21:14 +05:00
Ammar Ahmed
6bba171df0 mobile: add theme switch animation 2024-02-05 22:21:14 +05:00
Abdullah Atta
e52492488f web: fix all type errors 2024-02-05 22:21:14 +05:00
Abdullah Atta
bdaa2a5977 web: fix duplicate imports 2024-02-05 22:21:14 +05:00
Abdullah Atta
cd85cc32e2 core: fix type error 2024-02-05 22:21:14 +05:00
Abdullah Atta
1d7d5fd565 core: add tests for sync merger 2024-02-05 22:21:14 +05:00
Abdullah Atta
4baeee9429 core: migrate vault to a database collection 2024-02-05 22:21:14 +05:00
Ammar Ahmed
667a760c89 mobile: fix exporter and attachment download 2024-02-05 22:21:14 +05:00
Ammar Ahmed
da3ce62f08 global: fix type errors during build 2024-02-05 22:21:14 +05:00
Abdullah Atta
b5adf70853 web: use simple transform stream for export progress 2024-02-05 22:21:14 +05:00
Abdullah Atta
973c6e0d6f web: fix exporting 2024-02-05 22:21:14 +05:00
Ammar Ahmed
ca15a016fd mobile: add patches 2024-02-05 22:21:14 +05:00
Abdullah Atta
5cce557a14 web: update export note snapshots for linux 2024-02-05 22:21:14 +05:00
Abdullah Atta
e81ee6e4ce web: fix reminder tests 2024-02-05 22:21:14 +05:00
Abdullah Atta
339e3444e7 web: refresh notebooks after adding a new notebook 2024-02-05 22:21:14 +05:00
Abdullah Atta
a32df1a009 web: fix force redirect to home on navigating to a color 2024-02-05 22:21:14 +05:00
Abdullah Atta
cb7e438cd2 web: fix crash when sorting tags 2024-02-05 22:21:14 +05:00
Abdullah Atta
5cf5c1a7f7 web: fix crash when removing a notebook 2024-02-05 22:21:14 +05:00
Ammar Ahmed
35ae19acce mobile: sync with latest core changes 2024-02-05 22:21:14 +05:00
Abdullah Atta
e4130556e9 setup: optimize bootstrap script 2024-02-05 22:21:14 +05:00
Abdullah Atta
8dc25e9cac setup: only run npm rebuild if required 2024-02-05 22:21:14 +05:00
Abdullah Atta
90cc89cd6e core: remove unnecessary console.log 2024-02-05 22:21:14 +05:00
Abdullah Atta
4fc7c49ed5 setup: run npm rebuild after bootstrap 2024-02-05 22:21:14 +05:00
Abdullah Atta
387a05e24e setup: ignore scripts during bootstrap 2024-02-05 22:21:14 +05:00
Abdullah Atta
0129513ec6 setup: use 1 thread on CI 2024-02-05 22:21:14 +05:00
Ammar Ahmed
20e5f7babb mobile: fix search to use filtered selector for notes 2024-02-05 22:21:14 +05:00
Abdullah Atta
12dc4caff3 core: prevent recursive sync after pushing items 2024-02-05 22:21:14 +05:00
Abdullah Atta
58bab91bc8 web: simplify sync status 2024-02-05 22:21:14 +05:00
Abdullah Atta
78a25dccdd web: fix previewing trashed notes 2024-02-05 22:21:14 +05:00
Abdullah Atta
1ce15d8e50 core: fix trash 2024-02-05 22:21:14 +05:00
Abdullah Atta
4fb10e3507 web: improve sync test reliability 2024-02-05 22:21:14 +05:00
Abdullah Atta
f5e43f753f core: auto sync not triggered on note update 2024-02-05 22:21:14 +05:00
Abdullah Atta
f088ddf50b web: fix note history tests 2024-02-05 22:21:14 +05:00
Abdullah Atta
60974182e5 web: fix selection logic in list 2024-02-05 22:21:14 +05:00
Abdullah Atta
d1c613c2ec web: set testId on auth input fields 2024-02-05 22:21:14 +05:00
Abdullah Atta
676ffd1116 core: fix note content is not saving 2024-02-05 22:21:14 +05:00
Abdullah Atta
c759350b4c core: fix data is not iterable error on backup restore 2024-02-05 22:21:14 +05:00
Abdullah Atta
930fadeae4 global: update lockfiles 2024-02-05 22:21:14 +05:00
Abdullah Atta
3728179a5b core: add retry if a test fails 2024-02-05 22:21:14 +05:00
Abdullah Atta
0fa9649997 core: fix invalid object-id generation for some timestamps 2024-02-05 22:21:14 +05:00
Abdullah Atta
c8b2b14412 core: fix sync & monographs tests 2024-02-05 22:21:14 +05:00
Abdullah Atta
fe6007bc51 core: fix auto sync starting even when auto sync is disabled 2024-02-05 22:21:14 +05:00
Abdullah Atta
4df150d8a0 core: fix sync tests 2024-02-05 22:21:14 +05:00
Abdullah Atta
d62ee05e4b core: remove unnecessary migration success log 2024-02-05 22:21:14 +05:00
Abdullah Atta
a88d9ee134 core: fix Crypto is not supported on this platform. error during tests 2024-02-05 22:21:14 +05:00
Abdullah Atta
f77a95ca2b core: make all the core tests pass again 2024-02-05 22:21:14 +05:00
Abdullah Atta
5d2c6e0509 web: fix crash when note has no title 2024-02-05 22:21:14 +05:00
Abdullah Atta
8064cecc89 core: set default title if note has no title 2024-02-05 22:21:14 +05:00
Abdullah Atta
bf238ea560 web: update kysely patch 2024-02-05 22:21:14 +05:00
Abdullah Atta
b0c69b5d5a core: run patch-package after npm i 2024-02-05 22:21:14 +05:00
Abdullah Atta
82a0273cac web: run patch-package after npm i 2024-02-05 22:21:14 +05:00
Abdullah Atta
28edcadc22 web: more fixes 2024-02-05 22:21:14 +05:00
Abdullah Atta
6ab5264587 web: fix many type errors 2024-02-05 22:21:14 +05:00
Abdullah Atta
b732337ca2 global: update lockfiles 2024-02-05 22:21:14 +05:00
Abdullah Atta
c164ec4de7 theme: fix tinycolor2 not found error 2024-02-05 22:21:14 +05:00
Abdullah Atta
30f4fb3d9a web: fix notes search in tags, favorites etc 2024-02-05 22:21:14 +05:00
Abdullah Atta
aa12f5975f web: update wa-sqlite 2024-02-05 22:21:14 +05:00
Abdullah Atta
f3c2757b2a web: use memory tempStore for sqlite 2024-02-05 22:21:14 +05:00
Abdullah Atta
f1f67f871c web: add support for exporting sql database 2024-02-05 22:21:14 +05:00
Abdullah Atta
f352b8edec core: fix FTS5 SQLite search 2024-02-05 22:21:14 +05:00
Ammar Ahmed
a37dababe3 mobile: cleanup 2024-02-05 22:21:14 +05:00
Ammar Ahmed
fee13715f4 mobile: fix types 2024-02-05 22:21:14 +05:00
Ammar Ahmed
b046f17644 mobile: refactor zustand stores 2024-02-05 22:21:14 +05:00
Ammar Ahmed
890bb6e057 mobile: bug fixes 2024-02-05 22:21:14 +05:00
Ammar Ahmed
96ad097434 core: add fn to get cache item 2024-02-05 22:21:14 +05:00
Abdullah Atta
74e921930b web: only auto align title & headline 2024-02-05 22:21:14 +05:00
Abdullah Atta
e8eb0ee598 web: getKey -> key 2024-02-05 22:21:14 +05:00
Abdullah Atta
9fc7d644fc web: fix resolved item data misaligned with items 2024-02-05 22:21:14 +05:00
Abdullah Atta
2c6c7f7774 core: fix many issues with virtualized grouping 2024-02-05 22:21:14 +05:00
Ammar Ahmed
e9f5f22601 core: early return when items are empty 2024-02-05 22:21:14 +05:00
Abdullah Atta
2db4bd99b6 web: fix random crashes when scrolling in list 2024-02-05 22:21:14 +05:00
Abdullah Atta
65edcf8e6c web: replace react-virtual with react-virtuouso 2024-02-05 22:21:14 +05:00
Ammar Ahmed
8b927ca94f mobile: fix build ang bugs 2024-02-05 22:21:14 +05:00
Abdullah Atta
81fe67253a web: hide attachments access if user is logged out 2024-02-05 22:21:14 +05:00
Abdullah Atta
4cb418ff75 web: allow core to specify custom sql db name 2024-02-05 22:21:14 +05:00
Abdullah Atta
4cf281da08 web: close idb connection on sqlite connection close 2024-02-05 22:21:14 +05:00
Abdullah Atta
0682345db8 core: clear database on logout 2024-02-05 22:21:14 +05:00
Ammar Ahmed
f5613bd7ae core: add tempStore option to DB 2024-02-05 22:21:14 +05:00
Abdullah Atta
b121f75548 core: migrate items sequentially during sync 2024-02-05 22:21:14 +05:00
Abdullah Atta
1c837f5fc2 core: fix build error 2024-02-05 22:21:14 +05:00
Abdullah Atta
255bda8e61 web: execute only 1 query at a time 2024-02-05 22:21:14 +05:00
Abdullah Atta
c8c1871906 web: fix diff viewer crashing the app 2024-02-05 22:21:14 +05:00
Abdullah Atta
d56916fc33 core: do not stop sync if a conflict occurs 2024-02-05 22:21:14 +05:00
Abdullah Atta
a3fab3b5ca core: allow only 1 transaction at a time 2024-02-05 22:21:14 +05:00
Abdullah Atta
321bd2aa93 core: simplify item key derivation in virtualized grouping 2024-02-05 22:21:14 +05:00
Abdullah Atta
05ccfcd9fb web: improve list item rendering performance 2024-02-05 22:21:14 +05:00
Abdullah Atta
159c4454a3 core: always send realtime sync event 2024-02-05 22:21:14 +05:00
Abdullah Atta
371d7e527f core: fix content date edited not updating 2024-02-05 22:21:14 +05:00
Abdullah Atta
5de4e16b12 core: put items in chunks of 200 2024-02-05 22:21:14 +05:00
Abdullah Atta
bba809cef9 web: set name prop on fields 2024-02-05 22:21:14 +05:00
Abdullah Atta
384db9d27e core: implement sync v2 2024-02-05 22:21:14 +05:00
Ammar Ahmed
b236e7c9b8 mobile: use index based grouping 2024-02-05 22:21:14 +05:00
Abdullah Atta
fceb8a29c7 web: changes to use new grouping api 2024-02-05 22:21:14 +05:00
Abdullah Atta
c2791f2bbf core: implement new high perf indexed based grouping 2024-02-05 22:21:14 +05:00
Ammar Ahmed
4a7d3f5caf mobile: fix share extension in release 2024-02-05 22:21:14 +05:00
Abdullah Atta
e11fea58f6 web: fix router types 2024-02-05 22:21:14 +05:00
Abdullah Atta
ff33ced879 web: enable vite hot reloading 2024-02-05 22:21:14 +05:00
Abdullah Atta
5f7068c376 web: fix minor build issues 2024-02-05 22:21:14 +05:00
Abdullah Atta
c988c510ba web: migrate everything to react@18 2024-02-05 22:21:14 +05:00
Abdullah Atta
4917a888f1 web: fix notebook tests 2024-02-05 22:21:14 +05:00
Ammar Ahmed
3ba5250d59 mobile: share extension cleanup 2024-02-05 22:21:14 +05:00
Ammar Ahmed
d595913722 mobile: cleanup 2024-02-05 22:21:14 +05:00
Ammar Ahmed
1f94c47880 mobile: migrate share extension 2024-02-05 22:21:14 +05:00
Ammar Ahmed
c1b7c66e81 mobile: fix navigation 2024-02-05 22:21:14 +05:00
Abdullah Atta
8712dd286f web: send noteId when decrypting real-time locked content 2024-02-05 22:21:14 +05:00
Abdullah Atta
d836170b6f web: fix tag tests 2024-02-05 22:21:14 +05:00
Ammar Ahmed
0f2122bc41 mobile: pass tests 2024-02-05 22:21:14 +05:00
Abdullah Atta
77a5f83a84 web: fix html export snapshot 2024-02-05 22:21:14 +05:00
Abdullah Atta
f618f97845 core: fix md with frontmatter export 2024-02-05 22:21:14 +05:00
Ammar Ahmed
26bdab05dd core: fix ts error 2024-02-05 22:21:14 +05:00
Ammar Ahmed
ccd078f660 mobile: fix webpack build 2024-02-05 22:21:14 +05:00
Ammar Ahmed
74c4fdac21 mobile: push changes 2024-02-05 22:21:14 +05:00
Abdullah Atta
fc270459b0 web: fix notes tests 2024-02-05 22:21:14 +05:00
Abdullah Atta
a51f84c1f4 core: prevent duplicate colors based on color code 2024-02-05 22:21:14 +05:00
Abdullah Atta
c452e1524d core: fix error on locking notes 2024-02-05 22:21:14 +05:00
Ammar Ahmed
bc428dd11f mobile: push changes 2024-02-05 22:21:14 +05:00
Abdullah Atta
8311db0572 web: reset search results on navigation 2024-02-05 22:21:14 +05:00
Abdullah Atta
26d08615a7 web: reset prepared sql stmt on error 2024-02-05 22:21:14 +05:00
Abdullah Atta
130f39ef8a web: reuse search query 2024-02-05 22:21:14 +05:00
Abdullah Atta
4b83e7319b core: use porter trigram tokenizer for fts 2024-02-05 22:21:14 +05:00
Abdullah Atta
96f45bf61e core: properly escape fts query 2024-02-05 22:21:14 +05:00
Abdullah Atta
ebf20c31c8 web: add support for assigning tags to notes 2024-02-05 22:21:14 +05:00
Abdullah Atta
9c858c0037 core: fix "type" does not exist 2024-02-05 22:21:14 +05:00
Abdullah Atta
16b68aae4f core: fix build error 2024-02-05 22:21:14 +05:00
Ammar Ahmed
3179c0ca2a mobile: migrate to sqlite 2024-02-05 22:21:14 +05:00
Abdullah Atta
905c7cdf31 web: fix note editor properties 2024-02-05 22:21:14 +05:00
Abdullah Atta
dbeb6f7835 common: type is now optional in getFormattedDate 2024-02-05 22:21:14 +05:00
Abdullah Atta
72015fcbeb core: return FilteredSelector from attachments.ofNote 2024-02-05 22:21:14 +05:00
Abdullah Atta
d32f3994e8 web: show notebook & tag shortcuts when assigning notes 2024-02-05 22:21:14 +05:00
Abdullah Atta
eb9f99dc4b web: implement virtualized search for all items 2024-02-05 22:21:14 +05:00
Abdullah Atta
803ef1ca36 web: use lazy loading to load attachments 2024-02-05 22:21:14 +05:00
Abdullah Atta
9b420c200a web: load tag & color title lazily 2024-02-05 22:21:14 +05:00
Abdullah Atta
204d8f37fd web: show all nested notebooks as tree when moving notes 2024-02-05 22:21:14 +05:00
Abdullah Atta
3d202c834a web: add support for nested notebooks 2024-02-05 22:21:14 +05:00
Abdullah Atta
d5adb0c195 web: use new virtualized grouping for reminders 2024-02-05 22:21:14 +05:00
Abdullah Atta
fd4174d8d5 web: use new virtualized grouping for trash 2024-02-05 22:21:14 +05:00
Abdullah Atta
cb376a6144 web: use new virtualized grouping for monographs 2024-02-05 22:21:14 +05:00
Abdullah Atta
254db0fb49 core: add support for fetching trash as grouped 2024-02-05 22:21:14 +05:00
Abdullah Atta
34ec5c0eaa web: install missing @hazae41/foras 2024-02-05 22:21:14 +05:00
Abdullah Atta
9b31bb8598 core: fix attachment extraction for locked notes 2024-02-05 22:21:14 +05:00
Abdullah Atta
9f33de94cb global: update lockfiles 2024-02-05 22:21:14 +05:00
Abdullah Atta
f3cbcf47a5 web: start initial migration to sqlite 2024-02-05 22:21:14 +05:00
Abdullah Atta
ec7e81d5e8 ui: add support for lazily loading menu items 2024-02-05 22:21:14 +05:00
Abdullah Atta
200547415c common: remove total-notes utility 2024-02-05 22:21:14 +05:00
Abdullah Atta
4322559680 core: changes according to client needs 2024-02-05 22:21:14 +05:00
Abdullah Atta
de187ec433 core: fix database migration 2024-02-05 22:21:14 +05:00
Abdullah Atta
9e9a25861d core: add support for incremental grouping 2024-02-05 22:21:14 +05:00
Abdullah Atta
3ecf9eab3b core: fix size of nanoid in benchmarks 2024-02-05 22:21:14 +05:00
Abdullah Atta
a0f8666b7a core: optimize bson objectid generation & add benchmarks 2024-02-05 22:21:14 +05:00
Abdullah Atta
7cf8895b4d core: fix monographs 2024-02-05 22:21:14 +05:00
Abdullah Atta
84b786b5a3 core: fix sync tests 2024-02-05 22:21:14 +05:00
Abdullah Atta
b28ab28f57 core: remove temporary sqlite db 2024-02-05 22:21:14 +05:00
Abdullah Atta
70ef954de5 core: migrate everything to sqlite 2024-02-05 22:21:14 +05:00
Abdullah Atta
608761ee0d core: migrate all database writes to sqlite 2024-02-05 22:21:14 +05:00
Abdullah Atta
a70b309df8 global: update lockfiles 2024-02-05 22:21:14 +05:00
Abdullah Atta
58ef5050c4 core: remove outbox 2024-02-05 22:21:14 +05:00
Abdullah Atta
c1594c1687 core: convert everything else to typescript 2024-02-05 22:21:14 +05:00
Abdullah Atta
bcb8c14abf core: remove unnecessary sync tests 2024-02-05 22:21:14 +05:00
Abdullah Atta
adb325af4d core: migrate monographs to typescript 2024-02-05 22:21:14 +05:00
Abdullah Atta
315961eaae core: make migration tests less flaky 2024-02-05 22:21:14 +05:00
Abdullah Atta
65d3ef87d2 core: add tests for migrations 2024-02-05 22:21:14 +05:00
Abdullah Atta
d4d9ebf378 core: migrate settings to its own collection 2024-02-05 22:21:14 +05:00
Abdullah Atta
d90fa07ed8 core: get rid of topics; move to sub notebooks 2024-02-05 22:21:14 +05:00
Abdullah Atta
adae1b6187 core: migrate attachments to use relations for linking to notes 2024-02-05 22:21:14 +05:00
Abdullah Atta
b40f52b12b core: minor updates and fixes 2024-02-05 22:21:14 +05:00
Abdullah Atta
0b8e0f9848 core: fix types & syncing issues 2024-02-05 22:21:14 +05:00
ammarahm-ed
8cbb7f88d6 mobile: pass all tests 2024-02-05 22:21:14 +05:00
ammarahm-ed
17201979ba mobile: fix filesystem not loaded 2024-02-05 22:21:14 +05:00
ammarahm-ed
0bcd162559 mobile: fix navigating to tags 2024-02-05 22:21:14 +05:00
ammarahm-ed
fe2fc26bc0 mobile: migrate to typescript 2024-02-05 22:21:14 +05:00
ammarahm-ed
7b13d064e7 common: fix ts errors 2024-02-05 22:21:14 +05:00
Abdullah Atta
cad18cb9e8 mobile: use new types from core 2024-02-05 22:21:14 +05:00
Abdullah Atta
fa45ead4cb web: use new types from core 2024-02-05 22:21:14 +05:00
Abdullah Atta
e781273345 core: fix more typings 2024-02-05 22:21:14 +05:00
Abdullah Atta
d28179e0cd core: fix typing errors 2024-02-05 22:21:14 +05:00
Abdullah Atta
ee6a515683 core: convert almost everything to typescript 2024-02-05 22:21:14 +05:00
Abdullah Atta
37263304fa core: fix attachment mime type on read 2024-02-05 22:21:14 +05:00
Ammar Ahmed
e96ba80583 mobile: fix lint errors 2024-02-02 18:46:30 +05:00
Abdullah Atta
cfd80d2008 ci: update node version to 20 2024-02-02 18:35:42 +05:00
Abdullah Atta
288226a3af config: update nx 2024-02-02 18:32:53 +05:00
Abdullah Atta
4d2c9cfd4e desktop: set release channel to beta 2024-02-02 18:03:38 +05:00
Ammar Ahmed
fe52ce38e1 mobile: update ios version number 2024-02-02 15:51:05 +05:00
Ammar Ahmed
46acdf4279 ci: update workflow name
Signed-off-by: Ammar Ahmed <40239442+ammarahm-ed@users.noreply.github.com>
2024-02-02 15:39:46 +05:00
Ammar Ahmed
1f698280df mobile: v3 beta release 2024-02-02 15:32:11 +05:00
Ammar Ahmed
94babf2a97 ci: update iOS workflow
Signed-off-by: Ammar Ahmed <40239442+ammarahm-ed@users.noreply.github.com>
2024-02-02 15:12:47 +05:00
Ammar Ahmed
b19be80738 Update android.publish.internal.yml
Signed-off-by: Ammar Ahmed <40239442+ammarahm-ed@users.noreply.github.com>
2024-02-02 15:11:54 +05:00
Ammar Ahmed
f77746cf0e ci: android.publish.yml
Signed-off-by: Ammar Ahmed <40239442+ammarahm-ed@users.noreply.github.com>
2024-02-02 15:10:38 +05:00
Abdullah Atta
d3c033faf2 desktop: do not set teamId for notarization 2024-01-31 16:34:08 +05:00
Abdullah Atta
b9f22e1dd0 ci: APPLE_API_KEY_ISSUER > APPLE_API_ISSUER 2024-01-31 16:26:10 +05:00
Abdullah Atta
b0dd1c2095 desktop: fix unknown property 'notarize' error 2024-01-31 16:14:43 +05:00
Abdullah Atta
0beffd5165 desktop: use official electron builder notarization 2024-01-31 15:45:37 +05:00
Abdullah Atta
a1c0a834ee ci: update {upload,download}-artifact to v4 2024-01-31 15:34:10 +05:00
Abdullah Atta
27dc19cb58 ci: get rid of you may only define one of branches and branches-ignore for a single event 2024-01-31 15:20:21 +05:00
Abdullah Atta
896884ff59 ci: use m1 for building on mac 2024-01-31 15:19:10 +05:00
Abdullah Atta
97cb724bc1 ci: use wrangler directly to publish to cf 2024-01-31 14:52:18 +05:00
Abdullah Atta
f08e372bcf desktop: use app version from autoUpdater 2024-01-31 14:46:42 +05:00
Abdullah Atta
0aecb2d27a ci: update cloudflare action 2024-01-31 14:34:24 +05:00
Abdullah Atta
3735d9ee18 desktop: ready beta for deployment 2024-01-31 14:31:42 +05:00
Abdullah Atta
3d4264257e ci: deploy to correct cf pages project
Signed-off-by: Abdullah Atta <abdullahatta@streetwriters.co>
2024-01-31 12:53:37 +05:00
Abdullah Atta
96b97c4bec core: turn off sync push by default 2024-01-31 12:47:53 +05:00
Abdullah Atta
2f67611829 ci: add workflows for v3 beta publishing 2024-01-31 12:41:08 +05:00
Ammar Ahmed
058c612f4f ci: update default build tools version android
Signed-off-by: Ammar Ahmed <40239442+ammarahm-ed@users.noreply.github.com>
2024-01-30 11:41:20 +05:00
Ammar Ahmed
be8df90ccb iOS Publish Workflow (#4194)
Signed-off-by: Ammar Ahmed <40239442+ammarahm-ed@users.noreply.github.com>
2024-01-30 11:32:48 +05:00
Abdullah Atta
aa730b5b93 web: fix title only txt exports 2024-01-30 00:29:46 +05:00
Abdullah Atta
34bbcf6387 web: add feature highlights for v2.6.15 2024-01-29 23:33:15 +05:00
Abdullah Atta
33c3c78e2f desktop: get rid of configuration.win.signingHashAlgorithms should be an array 2024-01-29 23:27:45 +05:00
Ammar Ahmed
bf144dcda4 mobile: add ExportOptions.plist 2024-01-29 23:23:48 +05:00
Ammar Ahmed
284ebdb86f ci: @notesnook/ios
Signed-off-by: Ammar Ahmed <40239442+ammarahm-ed@users.noreply.github.com>
2024-01-29 23:23:01 +05:00
Abdullah Atta
9dac4cf7f4 desktop: set signing has to sha256 2024-01-29 23:22:29 +05:00
Abdullah Atta
fb973a5514 ci: run build job no matter what 2024-01-29 23:22:29 +05:00
Abdullah Atta
f9226510f9 desktop: use full path for FilesCatalog 2024-01-29 23:22:29 +05:00
Abdullah Atta
5c0a964699 ci: add inputs to disable build for specific platforms 2024-01-29 23:22:29 +05:00
Abdullah Atta
41ffaab3dc ci: install azurecodesigning module 2024-01-29 23:22:29 +05:00
Abdullah Atta
764c721f1d desktop: add windows code signing 2024-01-29 23:22:29 +05:00
Ammar Ahmed
3d267f91ef mobile: bump version code 2024-01-29 19:55:41 +05:00
Ammar Ahmed
f10f28a7f8 mobile: release v2.6.15 2024-01-29 19:32:14 +05:00
Ammar Ahmed
8d04f89a18 mobile: remove progress bar 2024-01-29 19:06:01 +05:00
Ammar Ahmed
9d1374dcd4 editor: fix progress not showing in image on mobile 2024-01-29 19:05:49 +05:00
Abdullah Atta
094a65e514 editor: hide progress on image if 0 2024-01-29 18:26:44 +05:00
Ammar Ahmed
372da979e1 mobile: support ignoring edits 2024-01-29 18:26:44 +05:00
Abdullah Atta
d3371326f2 editor: add progress indicator to image 2024-01-29 18:26:44 +05:00
Abdullah Atta
57452bd0e6 core: allow setting custom date edited 2024-01-29 18:26:44 +05:00
Abdullah Atta
993b8460bf web: do not update dateEdited if ignoring edits 2024-01-29 18:26:44 +05:00
Abdullah Atta
081201aa42 editor: add support for ignoring edits 2024-01-29 18:26:44 +05:00
Abdullah Atta
270e6778d1 editor: optimize image loading when fast scrolling 2024-01-29 16:23:59 +05:00
Abdullah Atta
2074320583 editor: revoke blob urls on image unmount 2024-01-29 16:09:05 +05:00
Abdullah Atta
cd90030616 web: fix note title in toolbar not changing 2024-01-29 15:45:13 +05:00
Abdullah Atta
807bd683b7 web: do not show success prompt on unlock cancel 2024-01-29 15:36:01 +05:00
Abdullah Atta
35929026a4 web: monograph.notesnook.com > monogr.ph 2024-01-29 15:22:22 +05:00
Ammar Ahmed
41eb94ee47 mobile: fix exporting locked notes 2024-01-29 11:02:32 +05:00
Ammar Ahmed
ba51e110ad mobile: fix opening webclips 2024-01-29 11:02:32 +05:00
Ammar Ahmed
ecad06d249 mobile: disable rtl mode 2024-01-29 11:02:32 +05:00
Abdullah Atta
19a151f637 editor: fix image query matching all images in editor 2024-01-28 17:17:45 +05:00
Ammar Ahmed
7c4a6f1e17 global: update deps 2024-01-27 23:06:24 +05:00
Ammar Ahmed
bc88e953d1 mobile: make sure tools render in toolbar 2024-01-27 23:05:30 +05:00
Ammar Ahmed
7d89ec676a mobile: fix attachments not loading when added from share ext 2024-01-27 22:50:41 +05:00
Ammar Ahmed
c8f8bcca93 mobile: organize bulk exports 2024-01-27 21:23:29 +05:00
Abdullah Atta
9fa904e849 web: fix exporting of single note 2024-01-27 21:23:29 +05:00
Abdullah Atta
05cc947cb4 web: fix pdf exports 2024-01-27 21:23:29 +05:00
alihamuh
a20a683b17 web: code refactoring 2024-01-27 21:23:29 +05:00
alihamuh
5fc43c931e web: added directory paths for exports 2024-01-27 21:23:29 +05:00
Abdullah Atta
0879c3b4d6 core: fix formatting of single spaced paragarphs in markdown exports (#4186)
* core: update showdown library to support single spaced paragraphs

* core: add tests

* core: update showdown library
2024-01-27 18:59:35 +05:00
Abdullah Atta
2eded71de3 core: fix restored attachments not syncing 2024-01-27 16:06:04 +05:00
Abdullah Atta
9e30ee50b8 core: add logs 2024-01-27 16:06:04 +05:00
Abdullah Atta
08621a5873 core: try to preserve mime type on backup restore 2024-01-27 16:06:04 +05:00
Abdullah Atta
17f5bd28ba web: do not overwrite filename & mimetype on attachment reupload 2024-01-27 16:06:04 +05:00
Ammar Ahmed
4558c0b2eb mobile: fix downloading attachments from editor 2024-01-27 16:06:04 +05:00
Abdullah Atta
a442677e91 editor: fix loading of images that have wrong mime type 2024-01-27 16:06:04 +05:00
Abdullah Atta
95666bee36 editor: remove attachment progress once it reaches 100 2024-01-27 16:06:04 +05:00
Abdullah Atta
c4232ac95a web: do not send hash when updating attachment 2024-01-27 16:06:04 +05:00
Abdullah Atta
0283037289 mobile: changes related to download cancellation 2024-01-27 16:06:04 +05:00
Abdullah Atta
b6fd0df715 web: get rid of all attachment loading logic 2024-01-27 16:06:04 +05:00
Abdullah Atta
f53fb58024 editor: eagerly load image/webclip instead of waiting on client 2024-01-27 16:06:04 +05:00
Abdullah Atta
21d452308d core: simplify download cancellation 2024-01-27 16:06:04 +05:00
Ammar Ahmed
823608cca1 mobile: daily trash cleanup 2024-01-27 16:05:28 +05:00
alihamuh
f3a0b998e4 web: added daily trash interval option 2024-01-27 16:05:28 +05:00
theProton
a47a38b16b web: fix incorrect title on scrolling note (#4137)
Signed-off-by: Parthiv-M <parthivmenon.dev@gmail.com>
2024-01-27 11:44:58 +05:00
Ammar Ahmed
a184c53dd5 mobile: fix loading empty conent in editor on tablets 2024-01-26 23:02:06 +05:00
Abdullah Atta
a3464255ec core: always use attachment mime type from backup if possible 2024-01-26 19:21:55 +05:00
Abdullah Atta
088c6670a9 core: fix crash if note title only contains whitespace 2024-01-26 19:12:41 +05:00
Abdullah Atta
27840b9bd6 editor: fix link pasting (#4178) 2024-01-26 18:57:59 +05:00
Muhammad Ali
ae819504f0 editor: fix input rules not working at the end of note (#4130)
* web: added condition in getDeletedNodes

* editor: some corrections

* editor: merge if conditions

---------

Co-authored-by: Abdullah Atta <abdullahatta@streetwriters.co>
2024-01-26 16:43:19 +05:00
Muhammad Ali
af792e394c web: add support for moving locked notes to trash (#4134)
* web: added support for single/multi trash notes

* web: show error when trying to preview a locked note in trash

---------

Co-authored-by: Abdullah Atta <abdullahatta@streetwriters.co>
2024-01-26 16:37:54 +05:00
Muhammad Ali
76a4d5c2a5 editor: fix subtasks getting unchecked on drag/drop reorder (#4135)
* editor: add a condition to check nested item state

* editor: force coerce value to boolean

---------

Co-authored-by: Abdullah Atta <abdullahatta@streetwriters.co>
2024-01-26 16:31:23 +05:00
Muhammad Ali
4e23acd608 web: fix realtime editor sync cannot be disabled (#4140) 2024-01-26 16:22:56 +05:00
Ammar Ahmed
3c65dcdd8b ci: bump android build tools version 2024-01-15 12:31:37 +05:00
Ammar Ahmed
efea6e3972 theme: add missing alpha colors 2024-01-15 12:31:11 +05:00
Ammar Ahmed
185cca7880 mobile: release v2.6.15 2024-01-12 20:10:58 +05:00
Abdullah Atta
fbded93405 global: update lockfiles 2024-01-05 22:10:05 +05:00
Abdullah Atta
674881b873 web: add zip64 support for backups, export & imports (#4079)
* web: use @zip.js/zip.js for reading zip files

* web: always use zip streams for writing zips

* core: fix types

* theme: update lockfile
2024-01-05 22:05:04 +05:00
Abdullah Atta
248fb6dd2d web: bump version to 2.6.14 2024-01-05 21:55:18 +05:00
Abdullah Atta
3211e7a149 global: update lockfiles 2024-01-05 21:54:06 +05:00
Abdullah Atta
08496c699e editor: fix font size changes back after exiting list (fixes #3602) 2024-01-05 21:48:00 +05:00
Abdullah Atta
060fa25814 core: support underline when exporting as md (fix #3990) 2024-01-05 20:53:14 +05:00
Abdullah Atta
a6b1effc77 core: remove relations auto cleanup 2024-01-05 16:24:02 +05:00
Abdullah Atta
5cf9c4808e web: show resubscribe button if subscription is canceled 2024-01-05 15:55:36 +05:00
Muhammad Ali
d5a44067fd web: fix editor header title input not becoming readonly (#4064) 2024-01-05 11:08:16 +05:00
Muhammad Ali
0de463526f editor: add support for +/- font-size using shortcut keys (#4029) 2024-01-05 10:40:29 +05:00
Muhammad Ali
5fd79875ef editor: fix math/formula block getting cut off on mobile (#4023) 2024-01-05 10:39:20 +05:00
Muhammad Ali
bdb19ad325 web: add license to settings (#4020) 2024-01-05 10:38:13 +05:00
Ammar Ahmed
7a12efc823 mobile: release v2.6.14 2023-12-22 11:58:34 +05:00
Abdullah Atta
2f3bddf860 web: bump version to 2.6.13 2023-12-22 11:51:11 +05:00
Abdullah Atta
6127eb97af editor: do not mark task list as completed if there are no items 2023-12-22 11:49:10 +05:00
Abdullah Atta
86386e14f3 web: update test snapshots 2023-12-22 11:41:42 +05:00
Abdullah Atta
9cc17c29f6 web: use Cmd-a on macOS to select all items in list 2023-12-22 11:38:40 +05:00
Abdullah Atta
a31e863c7b global: update lockfiles 2023-12-22 11:16:26 +05:00
Muhammad Ali
15a32ac83a editor: fix pasting a word inside a list adds newlines around it (#3843)
* editor: added openstart, openEnd from old slice

* editor: removed extra import

* editor: update test snapshots

---------

Co-authored-by: Abdullah Atta <abdullahatta@streetwriters.co>
2023-12-22 11:11:03 +05:00
Ammar Ahmed
fa5b84ec0d mobile: minor fixes 2023-12-22 11:10:19 +05:00
Muhammad Ali
7905584eac editor: disable invalid tools when inside a code block (#3997)
* editor: removed fontSize property from code-text style

* editor: TO BE CONTINUED

* editor: some comments for testing

* editor: comment removed

* editor: diabled font size and family in code-block

* editor: disable invalid tools when inside code block

* editor: remove unused console.log

---------

Co-authored-by: Abdullah Atta <abdullahatta@streetwriters.co>
2023-12-22 10:26:25 +05:00
Abdullah Atta
afc9b96a11 web: make importer error resistant 2023-12-21 09:08:04 +05:00
Abdullah Atta
4e6466fc18 editor: handle md links with parentheses (#3996) 2023-12-19 14:54:22 +05:00
Abdullah Atta
233baf78ce web: handle file picker dialog cancelation 2023-12-19 14:39:45 +05:00
Abdullah Atta
42d0bee37f desktop: bump electron to 25.9.8 2023-12-19 13:07:34 +05:00
Abdullah Atta
ccadab6a10 web: add md + frontmatter to export all options 2023-12-19 12:44:23 +05:00
Muhammad Ali
bfe5a5cb96 editor: correctly style other types of lists in outline list (#3889) 2023-12-19 12:15:36 +05:00
Muhammad Ali
88fc24c4cf editor: fix code blocks pasting in a single line without formatting (#3910)
* editor: added code support for w3school

* editor: fixed newline issue in per tag code
2023-12-19 12:13:59 +05:00
Abdullah Atta
8be4fea7a3 ui: use symbols for shortcut keys on macOS 2023-12-19 11:12:20 +05:00
Muhammad Ali
a3d39d8b6d editor: add Ctrl/Cmd+\ keyboard shortcut for "Clear all formatting" (#3989)
* editor: added short-cut key in keyap

* editor: use Mod key instead of Ctrl

---------

Co-authored-by: Abdullah Atta <abdullahatta@streetwriters.co>
2023-12-19 11:11:35 +05:00
Muhammad Ali
ff36f0259a editor: add support for converting md link syntax to link on paste (#3919) 2023-12-18 16:23:56 +05:00
Muhammad Ali
d13654088a editor: add support for checking/unchecking all items in a task list (#3975) 2023-12-18 16:21:44 +05:00
Muhammad Ali
8a6b97c757 editor: add special readonly mode for toolbar (#3913)
* editor: added a toolbar display on mobile in readonly mode

* editor: hide editable tools from readonly toolbar

---------

Co-authored-by: Abdullah Atta <abdullahatta@streetwriters.co>
2023-12-18 16:04:02 +05:00
Muhammad Ali
d71e0c109b web: going back in search after editing a notebook opens notebook edit dialog again (#3979)
* web: changed afterAction()

* web: fix back navigation for dialogs that replace

* web: remove unused import

---------

Co-authored-by: Abdullah Atta <abdullahatta@streetwriters.co>
2023-12-18 15:58:20 +05:00
Ammar Ahmed
7e345303fe mobile: fix app lock hides login page on enter background 2023-12-18 10:49:57 +05:00
Abdullah Atta
d881bdb77c web: fix attachment dialog not opening on error 2023-12-11 15:09:38 +05:00
Ammar Ahmed
4961102d8f mobile: fix subscriptions not verified on ios 2023-12-11 10:54:45 +05:00
Abdullah Atta
4f2ab4f1da docs: fix incorrect noun (fixes #3934)
Signed-off-by: Abdullah Atta <abdullahatta@streetwriters.co>
2023-12-07 09:56:55 +05:00
Ammar Ahmed
7540e34366 mobile: update provisioning profiles 2023-11-27 08:22:47 +05:00
Abdullah Atta
4d7a8b9ea0 web: bump version to 2.6.12 2023-11-25 22:20:39 +05:00
Abdullah Atta
812ecf93c8 web: fix tag tests 2023-11-25 22:18:05 +05:00
Ammar Ahmed
74d99d97bc mobile: release v2.6.12 2023-11-25 15:44:43 +05:00
Muhammad Ali
5bbc453268 web: fix new tags do not appear under "recent" tags (#3827) 2023-11-25 15:39:38 +05:00
Ammar Ahmed
d71b9edc7f mobile: add support for markdown + frontmatter 2023-11-25 15:32:31 +05:00
Abdullah Atta
134904b551 web: add md with frontmatter export 2023-11-25 15:32:31 +05:00
Abdullah Atta
3eeb292d70 core: add new md + frontmatter export format 2023-11-25 15:32:31 +05:00
Abdullah Atta
04125bbb98 web: minor ui fixes 2023-11-25 15:19:50 +05:00
Ammar Ahmed
cfc3a8341c mobile: update mmkv package 2023-11-25 15:19:27 +05:00
Abdullah Atta
cbb0a511a8 core: fix logs sorting 2023-11-25 15:19:27 +05:00
Abdullah Atta
dff1c7ed32 core: optimize logger 2023-11-25 15:19:27 +05:00
Abdullah Atta
7987ef81a6 web: use removeMulti to bulk remove old logs 2023-11-25 15:19:27 +05:00
Ammar Ahmed
8d8d4ee782 mobile: fix app freeze when deleting old logs 2023-11-25 15:19:27 +05:00
Ammar Ahmed
3a50c73cab core: add removeMulti to storage 2023-11-25 15:19:27 +05:00
Abdullah Atta
4ee0071eca core: stop localOnly items from always getting synced 2023-11-25 15:12:33 +05:00
Muhammad Ali
248192f404 editor: hide link popup in readonly mode on mobile (#3853) 2023-11-25 11:29:02 +05:00
Krishna Bharadwaj
e56b766c3a web: open testimonial source links in new tab (#3863)
Signed-off-by: Dharmavineta <krishnabharadwaj1995@gmail.com>
2023-11-25 11:27:24 +05:00
Abdullah Atta
170b2b24ab web: fix logged in status alignment 2023-11-25 11:26:19 +05:00
Ammar Ahmed
50137e0994 mobile: fix subscriptions not fulfilling on ios 2023-11-25 10:32:24 +05:00
Abdullah Atta
8b5babd5f8 desktop: use node.js fetch instead of net.fetch 2023-11-25 10:20:19 +05:00
Abdullah Atta
39e83200c8 web: fix attachment upload progress on desktop 2023-11-25 10:19:57 +05:00
Abdullah Atta
ead0e0107e core: allow deleting unuploaded attachments 2023-11-25 10:19:38 +05:00
Abdullah Atta
1c888bd4fd web: always open announcement link in browser on desktop app 2023-11-25 10:18:20 +05:00
Krishna Bharadwaj
ff92530339 web: hide email address from status bar (#3859) 2023-11-24 11:18:46 +05:00
Ammar Ahmed
ee38032403 mobile: release v2.6.11 2023-11-22 14:45:16 +05:00
Ammar Ahmed
2ce245659a mobile: fix pricing plans 2023-11-22 14:27:20 +05:00
Abdullah Atta
2c635eb4d7 web: bump version to 2.6.11 2023-11-22 10:44:39 +05:00
Abdullah Atta
d653223df0 editor: load images threadsafely 2023-11-22 10:36:48 +05:00
Muhammad Ali
290b0dfc78 web: close reminder dialog on setting snooze time (#3842) 2023-11-21 10:53:53 +05:00
Muhammad Ali
06bb9e654b web: fix weird janky animation when opening dialogs (#3826) 2023-11-21 08:20:26 +05:00
Muhammad Ali
576fe78e33 clipper: hide clip preview button when there's no clip data (#3772) 2023-11-21 08:18:28 +05:00
Abdullah Atta
0c16bfb5ff web: fix issue with service worker not getting registered 2023-11-20 12:34:07 +05:00
Abdullah Atta
a101dbdf31 web: use bloburl when previewing images 2023-11-20 11:12:02 +05:00
Abdullah Atta
682609044f editor: add support for previewing SVG images 2023-11-20 11:12:02 +05:00
Abdullah Atta
c0a7fb3a5c misc: remove unnecessary files 2023-11-20 10:01:08 +05:00
Abdullah Atta
a153aa791c web: bump version to 2.6.10 2023-11-20 09:49:31 +05:00
Abdullah Atta
76b3dd1f96 web: check if file exists before starting upload 2023-11-20 09:41:35 +05:00
Ammar Ahmed
8b9e2c8526 mobile: release v2.6.10 2023-11-20 08:44:42 +05:00
Ammar Ahmed
0dc2d14736 mobile: fix crash on pricing plans page on ios 2023-11-20 08:27:49 +05:00
Abdullah Atta
fc5dada701 editor: fix task items getting unchecked on sort 2023-11-20 08:25:27 +05:00
Abdullah Atta
f34060c4e0 editor: fix task list title getting reset 2023-11-20 08:25:27 +05:00
Abdullah Atta
006a239680 web: bump version to 2.6.9 2023-11-18 15:13:40 +05:00
Abdullah Atta
c6ed4bcab1 web: allow restoring backups when logged out 2023-11-18 15:13:05 +05:00
Abdullah Atta
b0fe967087 desktop: destroy system tray on toggle off setting 2023-11-18 14:53:14 +05:00
Abdullah Atta
3f32673b0a desktop: permanently disable menu bar 2023-11-18 14:45:17 +05:00
Ammar Ahmed
0525cecaee mobile: bump version code on android 2023-11-18 14:38:37 +05:00
Ammar Ahmed
7d84a9582b mobile: allow restoring backups when logged out 2023-11-18 14:38:37 +05:00
Abdullah Atta
289acd8ab6 web: add cross icon for removing tags 2023-11-18 14:35:57 +05:00
Abdullah Atta
bb6d9f604b web: wait 1s for math to render on pdf print 2023-11-18 14:23:57 +05:00
Abdullah Atta
c20a8232cd core: safe check for IS_DESKTOP_APP 2023-11-18 14:23:57 +05:00
Ammar Ahmed
c886212e89 core: add padding to math block in exports 2023-11-18 14:23:57 +05:00
Abdullah Atta
799ae111ae editor: disable resizing of floating images 2023-11-18 13:05:00 +05:00
Abdullah Atta
2a990979a0 editor: fix drag/drop of task items 2023-11-18 13:04:45 +05:00
Ammar Ahmed
2d1071117b mobile: release v2.6.9 2023-11-18 12:17:27 +05:00
Ammar Ahmed
aefce1ee67 mobile: add support /now /time in title 2023-11-18 12:17:27 +05:00
Ammar Ahmed
64d10f5050 mobile: reupload if file size is zero 2023-11-18 12:17:11 +05:00
Abdullah Atta
95b9edca62 web: check if attachment really exists on insertion 2023-11-18 12:17:11 +05:00
Abdullah Atta
06c8422cbe core: prevent duplicate attachments on backup restore 2023-11-18 12:17:11 +05:00
Abdullah Atta
177d8b5112 web: remove rogue console.log 2023-11-18 10:18:53 +05:00
Abdullah Atta
39afa98fce web: lazily load fs module 2023-11-18 10:17:23 +05:00
Abdullah Atta
5bc1ad4ba5 editor: fix url is undefined error in loading images 2023-11-18 10:16:59 +05:00
Abdullah Atta
0b1223976c web: fix builds 2023-11-18 10:03:14 +05:00
Muhammad Ali
3873758fe5 clipper(web): add fallback height & width for web clip(#3766) 2023-11-18 10:03:14 +05:00
Abdullah Atta
3c876cb1d8 global: update lockfiles 2023-11-18 10:03:13 +05:00
Abdullah Atta
0a1a16c197 editor: show placeholder when downloading image 2023-11-18 10:03:13 +05:00
Ammar Ahmed
3c56325fa6 mobile: fix image download 2023-11-18 10:03:13 +05:00
Abdullah Atta
e5db3353ae editor: improve image loading 2023-11-18 10:03:13 +05:00
Muhammad Ali
3416ba4eaa editor: removed a useState method 2023-11-18 10:03:13 +05:00
Muhammad Ali
cdb69bf553 editor: removed updateImage command from useEffect 2023-11-18 10:03:13 +05:00
Abdullah Atta
1aff6d0d55 editor: sync task list state at the very end 2023-11-18 10:03:13 +05:00
Ammar Ahmed
67b7bdcea3 mobile: update iap library to latest 2023-11-18 10:02:45 +05:00
Abdullah Atta
9b6a6de415 editor: update snapshots 2023-11-17 12:50:43 +05:00
Abdullah Atta
8325246a56 web: reduce network check retries to 3 2023-11-17 11:49:52 +05:00
Abdullah Atta
3d11fb5e3f web: specify if session has expired during login 2023-11-17 08:44:17 +05:00
Abdullah Atta
4627a58a52 web: show tags in readonly mode 2023-11-17 08:34:16 +05:00
Muhammad Ali
e557047af1 web: fix pressing enter to add a new tag from list of suggestion fails (#3548)
* web: added ref to control resturn key func

* web: click should open autosuggestions menu

---------

Co-authored-by: Abdullah Atta <abdullahatta@streetwriters.co>
2023-11-17 08:30:13 +05:00
Muhammad Ali
8b4cea7339 editor: make table header row immovable (#3459) 2023-11-16 15:30:32 +05:00
Muhammad Ali
ba5e35ec5c editor: fix resize for floating images (#3643)
* editor: removed maxWidth to make image float resizeable

* editor: fix resize for floating images

---------

Co-authored-by: Abdullah Atta <abdullahatta@streetwriters.co>
2023-11-16 15:26:25 +05:00
aryan katiyar
f53817e94d web: add link to source code in Settings (#3683) 2023-11-16 15:18:11 +05:00
Muhammad Ali
846efc5351 editor: use prosemirror plugin for managing task list state (#3252)
Co-authored-by: Abdullah Atta <abdullahatta@streetwriters.co>
2023-11-16 15:12:19 +05:00
Abdullah Atta
0149d00d64 web: add support for storing attachments in opfs or cache storage 2023-11-16 15:01:48 +05:00
Abdullah Atta
275f9730f1 web: allow downloading unuploading attachments 2023-11-16 15:01:48 +05:00
Abdullah Atta
43bc954989 core: allow uploading attachments that are in 0 notes 2023-11-16 15:01:48 +05:00
Abdullah Atta
c88bc722e1 fs: get rid of localforage and use generic interface 2023-11-16 15:01:48 +05:00
Muhammad Ali
ec6fc6839e editor: support datetime shortcuts in task list title & note title (#3746)
Co-authored-by: Abdullah Atta <abdullahatta@streetwriters.co>
2023-11-16 14:14:43 +05:00
Abdullah Atta
8383c93292 desktop: only set openAtLogin if not already set 2023-11-16 13:07:21 +05:00
Muhammad Ali
37d8d85935 clipper: delete import css rules after iteration (#3720)
Co-authored-by: Abdullah Atta <abdullahatta@streetwriters.co>
2023-11-16 13:06:51 +05:00
Muhammad Ali
95db7c6329 desktop: fix minimize to system tray not working immediately after enabling (#3734)
Co-authored-by: Abdullah Atta <abdullahatta@streetwriters.co>
2023-11-16 13:01:22 +05:00
Muhammad Ali
636783335f desktop: add setting to disable menu bar (#3728)
Co-authored-by: Abdullah Atta <abdullahatta@streetwriters.co>
2023-11-16 09:51:01 +05:00
Abdullah Atta
f88431ded4 core: handle case where headline is undefined 2023-11-03 08:28:41 +05:00
Abdullah Atta
91b0de0580 web: bump version to 2.6.8 2023-11-02 13:23:00 +05:00
Abdullah Atta
a5704ad75c desktop: do not show auto update toggle for flatpak users 2023-11-02 13:20:51 +05:00
Abdullah Atta
ba8c09a7d9 desktop: update electron to 25.9.3 2023-11-02 13:07:15 +05:00
Abdullah Atta
dbe4015655 web: preserve note edited time when exporting 2023-11-02 12:59:47 +05:00
Abdullah Atta
76237ad7dd web: fix sync progress in account recovery 2023-11-02 10:35:06 +05:00
Abdullah Atta
dbfb1168fb core: fix restored notes not syncing (closes #3472) 2023-11-02 10:34:21 +05:00
Abdullah Atta
830d28fbe5 editor: fix pasting link replaces selected text
fixes #3520
2023-10-31 12:33:49 +05:00
Abdullah Atta
c6def2a8fe web: fix crash on opening editor properties
(fixes #3585)
2023-10-31 12:03:45 +05:00
Abdullah Atta
ebc95fcea7 web: make duplicate filenames in attachments unique on download (#3678)
* web: make duplicate filenames in attachments unique

* Update apps/web/src/utils/streams/attachment-stream.ts

Signed-off-by: Abdullah Atta <thecodrr@protonmail.com>

* web: make attachment filename case insensitive

---------

Signed-off-by: Abdullah Atta <thecodrr@protonmail.com>
2023-10-31 11:35:01 +05:00
Abdullah Atta
f78cfe59e8 web: hide note title immediately on setting change 2023-10-31 11:12:11 +05:00
Johnny Avila
a2cbc93d34 web: add setting to allow hiding note title from window title (#3480)
Signed-off-by: Johnny Avila <avila.johnny11@gmail.com>
Co-authored-by: Abdullah Atta <abdullahatta@streetwriters.co>
2023-10-31 11:08:14 +05:00
Muhammad Ali
4728f14e8c core: add year when grouping by month (#3630) 2023-10-31 10:57:52 +05:00
Abdullah Atta
e94b431d58 web: change pwa theme color according to current theme (#3621)
* web: change pwa theme color according to current theme

* web: use background color as pwa theme-color
2023-10-31 10:56:37 +05:00
Muhammad Ali
0ac5e2b995 web: hide unnecessary items from status bar in focus mode (#3577)
* web: used app-store to hide sync-status suring focus mode

* web: hide useless items from status bar in focus mode

---------

Co-authored-by: Abdullah Atta <abdullahatta@streetwriters.co>
2023-10-31 10:54:57 +05:00
Muhammad Ali
365764e3e3 web: update vault setting immediately after vault creation (#3542)
* web: added onStateChange function to vault-settings.

* web: fix font style of checkbox in password dialog

---------

Co-authored-by: Abdullah Atta <abdullahatta@streetwriters.co>
2023-10-31 10:50:38 +05:00
Ammar Ahmed
8d2b5aa3de mobile: release v2.6.8 2023-10-30 15:24:30 +05:00
Ammar Ahmed
9a031a4a43 mobile: fix selection state causing delete to fail 2023-10-30 14:25:14 +05:00
Ammar Ahmed
d412679fd7 mobile: fix relogin in session expired mode 2023-10-30 11:56:52 +05:00
Ammar Ahmed
22c52d4432 core: reset lastSynced if new login 2023-10-30 11:55:43 +05:00
Ammar Ahmed
9e5bdf9e33 core: fix access token not saved 2023-10-30 11:55:14 +05:00
Abdullah Atta
7253eca5c0 core: do not allow saving empty token response 2023-10-30 10:55:39 +05:00
Abdullah Atta
b1f3dc29c5 core: use single sse command for logging the user out 2023-10-30 10:55:39 +05:00
Abdullah Atta
8e3df8d41b editor: disallow links in inline code 2023-10-30 10:41:04 +05:00
Ammar Ahmed
efad22c00a mobile: fix 24 hour reminder time 2023-10-30 10:26:15 +05:00
Ammar Ahmed
b7690f13b1 mobile: hide pinned notification subtitle 2023-10-30 09:58:52 +05:00
Ammar Ahmed
6e1739da2e mobile: fix note not added to default notebook in widget 2023-10-24 17:35:53 +05:00
Ammar Ahmed
77ab423041 mobile: fix keep in view in editor (#3612) 2023-10-24 16:54:00 +05:00
Ammar Ahmed
96a8e885d3 Fix session expiry dialog not presented when user encryption key is not found (#3625)
* core: fix session expired dialog not shown when
user encryption key does not
exist

* core: add watch command
2023-10-24 15:58:26 +05:00
Ammar Ahmed
2b917c7eca mobile: fix pin input hide behind keyboard on session expiry sheet (#3624) 2023-10-24 15:57:20 +05:00
Ammar Ahmed
942d2f7a4e mobile: fix system bar colors (#3611) 2023-10-24 12:44:41 +05:00
Ammar Ahmed
75142ff1b2 mobile: fix session id mismatch causing editor to get stuck.(#3613) 2023-10-24 09:18:55 +05:00
Abdullah Atta
74172dc3fa docs: update organizing notes using notebooks doc 2023-10-03 14:42:54 +05:00
Abdullah Atta
848fdda8d7 docs: update outdated documentation 2023-10-03 13:44:21 +05:00
Abdullah Atta
3895569a0c setup: make flatpak source generation more robust 2023-10-03 12:02:27 +05:00
Abdullah Atta
0aac29ca76 web: bump version to 2.6.7 2023-10-02 21:06:08 +05:00
Abdullah Atta
25f9eece8d web: remove blur from dialogs 2023-10-02 21:06:08 +05:00
Ammar Ahmed
0e22b8ab2c mobile: release v2.6.7 2023-10-02 19:01:16 +05:00
Abdullah Atta
577c2ce6cd core: auto create vault if non exists & user opens a locked note 2023-10-02 18:00:36 +05:00
Abdullah Atta
5b9cb8ca4b desktop: fix close to system tray only works once 2023-10-02 17:56:41 +05:00
Ammar Ahmed
2eda93c0c2 mobile: fix add to notebook sheet hangs with many notebooks (#3505)
Signed-off-by: Ammar Ahmed <40239442+ammarahm-ed@users.noreply.github.com>
2023-10-02 17:06:28 +05:00
Ammar Ahmed
d916adf2e3 mobile: fix dialog show above app lock 2023-10-02 17:05:41 +05:00
Abdullah Atta
1f065bcbb2 web: do not show save dialog when Ctrl+S is pressed 2023-10-02 16:37:09 +05:00
Abdullah Atta
3efdd9505e editor: do not open keyboard on task item drop 2023-10-02 16:22:20 +05:00
Abdullah Atta
ab3f5f6a6c editor: hide scrollbar in math nodes 2023-10-02 16:17:39 +05:00
Muhammad Ali
9027be7bbb web: add menu item to allow copying a note as text or markdown (#3193)
Co-authored-by: Abdullah Atta <abdullahatta@streetwriters.co>
2023-10-02 16:09:35 +05:00
Abdullah Atta
7b08b5c035 core: fix vault key not getting synced 2023-10-02 16:07:52 +05:00
Abdullah Atta
4a557c67b7 core: fix require prismjs on desktop build 2023-10-02 15:28:47 +05:00
Abdullah Atta
a6ebe092e8 core: fix undefined is not an object when publishing empty note 2023-10-02 15:15:21 +05:00
Midhun Sudhir
9759e1b396 mobile: fix typo in pricing plans (#3477)
Signed-off-by: Midhun Sudhir <60651970+midhun3301@users.noreply.github.com>
2023-09-28 23:19:15 +05:00
Ammar Ahmed
b6147ca5a1 mobile: release v2.6.6 2023-09-25 13:26:40 +05:00
Ammar Ahmed
83887db77a mobile: pass e2e tests 2023-09-25 13:26:40 +05:00
Abdullah Atta
1facfbec0e web: bump version to 2.6.6 2023-09-25 12:38:49 +05:00
Abdullah Atta
d6608325b4 web: skip education plan in tests 2023-09-25 12:27:09 +05:00
Ammar Ahmed
439b090470 mobile: fix learn more button is empty 2023-09-25 12:23:37 +05:00
Ammar Ahmed
ed98319a21 mobile: fix topic sheet size 2023-09-25 12:23:37 +05:00
Ammar Ahmed
54f94f44c0 mobile: remove icon animation 2023-09-25 12:23:37 +05:00
Ammar Ahmed
57cf0e23cf core: fix sorting of debug logs 2023-09-25 12:23:37 +05:00
Abdullah Atta
9e66269aca core: fix core tests 2023-09-25 12:20:54 +05:00
Abdullah Atta
a4f50fa2c3 web: fix backup tests 2023-09-25 11:47:29 +05:00
Abdullah Atta
7a0bafb02c editor: improve drag/drop support in task lists 2023-09-25 11:06:05 +05:00
Abdullah Atta
a24658ac0e core: fix "Cannot read property type of undefined" 2023-09-25 10:43:54 +05:00
Abdullah Atta
bb77385b66 editor: update tiptap to 2.1.11 2023-09-25 10:36:36 +05:00
Ammar Ahmed
e8b1bb649c editor: focus editor on press enter on mobile 2023-09-22 17:32:01 +05:00
Ammar Ahmed
023347da3f editor: fix link color in tool 2023-09-22 17:31:36 +05:00
Ammar Ahmed
f10146be03 mobile: fix image preview not loading image 2023-09-22 17:17:25 +05:00
Ammar Ahmed
7bac3c1326 mobile: fix selection is not reset when switching topics 2023-09-22 16:35:40 +05:00
Ammar Ahmed
528c0a10de mobile: skip clipping images from webpage 2023-09-22 10:32:52 +05:00
Ammar Ahmed
c1e1a8d1a9 clipper: support full page clips without styles and images 2023-09-22 10:32:52 +05:00
Ammar Ahmed
21720a510d editor: cache src of image when loading from network 2023-09-22 10:32:52 +05:00
Ammar Ahmed
a2d5e7f32f editor: layout images as block on mobile 2023-09-22 10:32:52 +05:00
Ammar Ahmed
629fb7b9cd mobile: patch lib to support asset reading 2023-09-22 10:32:52 +05:00
Ammar Ahmed
f6a4af8c00 clipper: convert relative urls to absolute 2023-09-22 10:32:52 +05:00
Ammar Ahmed
837e60316a mobile: fix loading clip on android 2023-09-22 10:32:52 +05:00
Ammar Ahmed
2592a90853 mobile: fix clipper build output path 2023-09-22 10:32:52 +05:00
ammarahm-ed
788429efb7 mobile: clip dynamic websites 2023-09-22 10:32:52 +05:00
Ammar Ahmed
674d8672f0 mobile: set title max length to 500 chars 2023-09-21 15:29:47 +05:00
ammarahm-ed
8d371fce79 mobile: multi-line title 2023-09-21 15:29:47 +05:00
Muhammad Ali
f0f391ec73 editors: add support for write protecting a specific task list (#3285)
* editor: added readonly attr for tasklist

* Update packages/editor/src/extensions/task-list/task-list.ts

Signed-off-by: Abdullah Atta <thecodrr@protonmail.com>

* editor: fixed tasklist title input

* editor: allow write protecting nested task lists

---------

Signed-off-by: Abdullah Atta <thecodrr@protonmail.com>
Co-authored-by: Abdullah Atta <abdullahatta@streetwriters.co>
2023-09-21 14:52:55 +05:00
Muhammad Ali
abc3b5569c core: fix upcoming reminder time calculation (#3341)
* core: added missing equality sign

* core: added test
2023-09-21 14:04:20 +05:00
Muhammad Ali
c42dc0f12e editor: add drag/drop support for attachments (#3325)
* editor: added drag for attachments

* editor: refactor attachment dragging check

---------

Co-authored-by: Abdullah Atta <abdullahatta@streetwriters.co>
2023-09-21 13:59:41 +05:00
luzpaz
7ec14f2eff mobile: fix typos in apps/mobile subdirectory
Found via `codespell -q 3 -S "*.patch"`
2023-09-21 13:51:28 +05:00
ammarahm-ed
31d4e88efd mobile: check if file exists based on file size 2023-09-21 13:43:16 +05:00
Abdullah Atta
da06184d51 core: increment progress even if file exists 2023-09-21 13:43:16 +05:00
Abdullah Atta
08296793f1 web: improve attachment progress events & ui 2023-09-21 13:43:16 +05:00
Abdullah Atta
3b6fde2ea6 web: check file size when checking for file existence 2023-09-21 13:43:16 +05:00
Abdullah Atta
1f2948c9a5 core: check for file existence before calling downloadFile 2023-09-21 13:43:16 +05:00
ammarahm-ed
e62267042e mobile: add support for progress 2023-09-21 13:43:16 +05:00
ammarahm-ed
883fe48946 core: add download start & canceled events 2023-09-21 13:43:16 +05:00
Abdullah Atta
6ba18c5bd1 web: use new file upload/download events 2023-09-21 13:43:16 +05:00
Abdullah Atta
207347190d core: fix download/upload cancellation 2023-09-21 13:43:16 +05:00
Ammar Ahmed
0cf76e1f11 mobile: fix sorting notes in topics (#3348)
* mobile: fix sorting notes in topics

* mobile: fix list is not updated
2023-09-21 13:30:10 +05:00
ammarahm-ed
9d76311f82 mobile: support font scaling 2023-09-21 13:29:44 +05:00
ammarahm-ed
c327a292c7 mobile: show +, # and export icon in selection header 2023-09-21 13:24:25 +05:00
ammarahm-ed
5a965f6d52 mobile: simplify deleting topic 2023-09-21 13:24:25 +05:00
ammarahm-ed
a81c621d49 mobile: migrate to persisted app cache
We now use app files directory on android
& LibraryPath on iOS to store cache files
such as images etc so they don't get deleted
by the system
2023-09-21 13:19:46 +05:00
ammarahm-ed
2e7b5d0d08 mobile: fix attachments fail to encrypt from share extension 2023-09-21 13:19:46 +05:00
ammarahm-ed
6d324fe8da mobile: improve editor content loading 2023-09-21 13:10:55 +05:00
ammarahm-ed
6d2be3a083 mobile: clear download/upload progress on cancel token 2023-09-21 13:10:55 +05:00
ammarahm-ed
d252c6fc5c mobile: fix editor not reset session id 2023-09-21 13:10:55 +05:00
ammarahm-ed
30c5e87c67 mobile: support running pending sync 2023-09-21 13:06:38 +05:00
Abdullah Atta
2db819de2b web: add support for running pending sync 2023-09-21 13:06:38 +05:00
Muhammad Ali
bff5e64484 editor: fix plain text pasting on windows (#3402)
* editor: added check for empty <p/> with <br/>

* editor: improve paragraph empty check

* editor: add tests

* editor: fix CRLF line breaks when pasting text

---------

Co-authored-by: Abdullah Atta <abdullahatta@streetwriters.co>
2023-09-21 11:58:55 +05:00
Abdullah Atta
3492bef2c7 crypto: fix nodejs native encryption causing file corruption 2023-09-21 11:30:47 +05:00
Abdullah Atta
6d9aa56084 web: fix loading indicator in setting buttons 2023-09-21 11:30:23 +05:00
Abdullah Atta
94c40b3444 desktop: fix jumplist item activation when app is running 2023-09-21 11:30:23 +05:00
Abdullah Atta
278c8b1115 web: use update status in settings about screen 2023-09-21 11:30:23 +05:00
Abdullah Atta
8e8581cce5 desktop: auto check for updates every 12 hours 2023-09-21 11:30:23 +05:00
Abdullah Atta
bd29f4e9e2 web: hide "You are on latest version" notice 2023-09-21 11:30:23 +05:00
Abdullah Atta
eb2b92d85c web: only show loading indicator on button that was pressed 2023-09-21 11:30:23 +05:00
Abdullah Atta
042ac0d338 desktop: fix auto updater events not sent to app 2023-09-21 11:30:23 +05:00
Abdullah Atta
a7011f0d6b global: update @trpc/* to 10.38.3 2023-09-21 11:30:23 +05:00
Abdullah Atta
22144ee18d web: fix backup validation check 2023-09-19 22:28:16 +05:00
Abdullah Atta
b85484fceb desktop: update lockfile 2023-09-15 17:13:47 +05:00
Abdullah Atta
b58956371f desktop: bump electron to v25.8.1 fixing CVE-2023-4863 2023-09-15 17:10:54 +05:00
Abdullah Atta
f487e9f7e2 global: update lockfiles 2023-09-15 15:06:27 +05:00
Abdullah Atta
f14efd3b1e web: fallback to blob download if service worker not found 2023-09-15 11:29:40 +05:00
Abdullah Atta
3496a42c19 web: bump to version 2.6.5 2023-09-15 10:24:15 +05:00
Abdullah Atta
0882c3ecf5 web: use single service worker for everything 2023-09-15 10:23:56 +05:00
ammarahm-ed
1f6d76bac7 mobile: update versionCode 2023-09-15 00:06:42 +05:00
ammarahm-ed
db7c3f4321 mobile: release v2.6.5 2023-09-14 23:45:44 +05:00
ammarahm-ed
9ea4651fb0 core: fix backup fail if item in null/undefined 2023-09-14 23:45:13 +05:00
Abdullah Atta
ed5a75447f web: register stream saver service worker on app start 2023-09-14 19:03:15 +05:00
ammarahm-ed
d08994a314 mobile: bump version code 2023-09-13 18:51:36 +05:00
ammarahm-ed
2bf6e7073c core: fix migration not running on colors 2023-09-13 18:21:55 +05:00
ammarahm-ed
7990458cf3 core: call migrateItem with backup key 2023-09-13 18:15:44 +05:00
ammarahm-ed
f3b13735f3 mobile: fix color of button in sheet 2023-09-13 18:14:03 +05:00
ammarahm-ed
ae680a32bc core: fix migration 2023-09-13 17:32:17 +05:00
ammarahm-ed
cbd0f18c6e mobile: release 2.6.4 2023-09-13 14:25:24 +05:00
ammarahm-ed
fd62b1b631 mobile: fix backup not created before migration 2023-09-13 13:50:05 +05:00
ammarahm-ed
2159f6720e mobile: fix delete notebook in bulk 2023-09-13 13:46:08 +05:00
ammarahm-ed
67c3a36464 editor: fix code-block ui 2023-09-13 13:44:53 +05:00
Abdullah Atta
652efc8624 ci: only get app store version if publishing to app store 2023-09-13 13:07:14 +05:00
Abdullah Atta
30da041ea3 web: ask for password if backup encryption if off 2023-09-13 12:48:58 +05:00
Abdullah Atta
e90c401503 web: bump version to 2.6.4 2023-09-13 12:47:10 +05:00
Abdullah Atta
83d919f06e web: fix login form shown for split second before login 2023-09-13 12:42:54 +05:00
Abdullah Atta
74fef0d2d9 core: run all items migrator before other migrators 2023-09-13 12:42:02 +05:00
Abdullah Atta
5906b24da1 core: add support for localOnly migrations 2023-09-13 12:40:30 +05:00
Abdullah Atta
8dd36f7adf core: set last synced to 0 right after login 2023-09-13 12:39:15 +05:00
ammarahm-ed
f2dc26dbff mobile: add support for nnbackupz format 2023-09-13 11:42:59 +05:00
ammarahm-ed
87915ecd00 core: handle null/undefined keys in backup 2023-09-13 11:41:49 +05:00
Abdullah Atta
3cf4e55621 core: restore colors from backups correctly 2023-09-13 10:45:40 +05:00
Abdullah Atta
4c7cd840df core: fix indexes getting replaced on backup restore 2023-09-13 10:23:44 +05:00
Abdullah Atta
bd238a6fe4 core: remove unnecessary exist check slowing reads 2023-09-13 10:23:44 +05:00
Abdullah Atta
beaace9b54 web: add support for streaming backup creation & restore 2023-09-13 10:23:44 +05:00
Abdullah Atta
c5352c2a73 core: add support for restoring new nnbackupz format 2023-09-13 10:23:44 +05:00
Abdullah Atta
dd6410c33e core: add support for streaming backups 2023-09-13 10:23:44 +05:00
Abdullah Atta
ac9dca8098 core: migrate db in a streaming way 2023-09-13 10:23:44 +05:00
Abdullah Atta
74ade6fecc core: make sure items sync after migration 2023-09-13 10:23:44 +05:00
Abdullah Atta
e1cfbb155d core: fix Cannot read properties of null (reading 'clear') 2023-09-13 10:23:44 +05:00
Abdullah Atta
030aba2ac3 core: add migration to remove item.remote 2023-09-13 10:23:44 +05:00
Abdullah Atta
9ab7e722e0 core: port fixes for sync issues from typescript branch 2023-09-13 10:23:44 +05:00
ammarahm-ed
14d6714209 theme: confirm before downloading code-block css 2023-09-11 12:51:01 +05:00
ammarahm-ed
e8e50bebcf theme: support code-block css in builder 2023-09-11 12:33:14 +05:00
ammarahm-ed
fa0610e392 mobile: release v2.6.3 2023-09-09 11:54:56 +05:00
Abdullah Atta
124489a5a5 web: bump to version 2.6.3 2023-09-09 11:06:56 +05:00
Abdullah Atta
22fcdc0fb1 desktop: bump to version 2.6.3 2023-09-09 11:06:20 +05:00
Abdullah Atta
1375300a91 desktop: downgrade electron to v25.8.0 2023-09-09 11:05:25 +05:00
Abdullah Atta
e6f4aea4d7 desktop: handle unhandled errors 2023-09-08 14:49:02 +05:00
Abdullah Atta
d72c4057ff theme: support ARGB values in background 2023-09-08 09:45:54 +05:00
Abdullah Atta
02d80ee4fa web: add separate plan for education discount 2023-09-07 18:39:21 +05:00
ammarahm-ed
2da3e9e0be mobile: release v2.6.2 2023-09-06 21:38:20 +05:00
ammarahm-ed
8333e7bdb3 mobile: set NSExtensionActivationDictionaryVersion to v2 2023-09-06 20:07:15 +05:00
ammarahm-ed
cef2472aae mobile: handle state when app entered background
to take some action like pick a file/image etc.
2023-09-06 20:06:46 +05:00
ammarahm-ed
a4946f7e89 mobile: hide caret when input 2fa code 2023-09-06 20:05:06 +05:00
ammarahm-ed
3e95e0feae mobile: fix db logger not logging 2023-09-06 20:04:46 +05:00
Abdullah Atta
b81843cdf7 core: sync settings before everything else 2023-09-06 19:31:06 +05:00
Abdullah Atta
eedd50fc64 web: bump version to 2.6.2 2023-09-06 17:06:02 +05:00
Abdullah Atta
1cb00bca0d core: fix collector tests 2023-09-06 17:00:04 +05:00
Abdullah Atta
8c233570a7 core: return default values for unset settings 2023-09-06 16:42:51 +05:00
Muhammad Ali
8a3c8b85bb web: fix autocomplete hint in signup form password fields (#3272) 2023-09-06 16:09:54 +05:00
Abdullah Atta
2ee8a807dc core: fix settings getting replaced on login 2023-09-06 15:56:20 +05:00
Abdullah Atta
ec85f748fa core: use serverLastSynced time from client 2023-09-06 15:43:20 +05:00
Abdullah Atta
051b7fcb8c core: notify autosync before setting remote= false 2023-09-06 15:28:56 +05:00
Abdullah Atta
9af88811d8 core: remove remote: true from settings after sync 2023-09-06 15:09:31 +05:00
Abdullah Atta
6c4672037c web: do not run sync on file picker closed 2023-09-06 14:49:56 +05:00
Abdullah Atta
d59e0d9ee2 core: sync attachments before anything else 2023-09-06 14:48:43 +05:00
Abdullah Atta
5a001a1457 core: use bulk io operations for logger storage 2023-09-06 12:30:39 +05:00
Abdullah Atta
44d6474b36 core: fix attachments not syncing 2023-09-06 12:14:03 +05:00
Abdullah Atta
0607e005f5 core: only sync syncable attachments 2023-09-06 12:04:58 +05:00
Abdullah Atta
308eb4b55f core: merge remote attachment if local attachment doesn't exist 2023-09-06 11:40:44 +05:00
Abdullah Atta
6abb6207c5 core: always keep remote attachment if it is newer 2023-09-06 11:16:53 +05:00
Abdullah Atta
89189d52ae web: add log on when sync is ignored 2023-09-06 10:28:31 +05:00
Abdullah Atta
e653ce69f8 core: push items serially tcp style 2023-09-06 10:22:53 +05:00
Abdullah Atta
9b89b328f6 core: send sync progress event after merge 2023-09-06 09:58:07 +05:00
Abdullah Atta
77ae71fab1 web: fix sync status progress 2023-09-06 09:40:24 +05:00
Abdullah Atta
7f7c1e6b93 core: use linkedom and node-fetch for tests 2023-09-06 09:40:24 +05:00
ammarahm-ed
30341bea1c mobile: run sync based on lastSyncTime 2023-09-06 09:27:02 +05:00
Abdullah Atta
82d44ea890 web: show sync status in place of placeholder 2023-09-05 20:15:34 +05:00
Abdullah Atta
5f0907168f core: always send sync request to client 2023-09-05 18:01:44 +05:00
Abdullah Atta
48061962b1 web: fix backup/export tests 2023-09-05 17:47:28 +05:00
Abdullah Atta
24141b66cf core: add log to debug relations removal issue 2023-09-05 17:47:11 +05:00
Abdullah Atta
41beee45ba core: migrate in bulk for more performance 2023-09-05 16:08:10 +05:00
Abdullah Atta
cb7c70b0d6 web: fix navigation menu ui 2023-09-05 15:47:39 +05:00
ammarahm-ed
2aa5a57557 mobile: use accentForeground on accent background 2023-09-05 15:40:25 +05:00
Abdullah Atta
17582bcefb web: fix navigation menu not taking up full space 2023-09-05 15:40:25 +05:00
Abdullah Atta
1f5a5afb10 theme: do not throw error if deprecated color is found 2023-09-05 15:40:25 +05:00
Abdullah Atta
d1c724563f web: fix themes selection not showing themes 2023-09-05 15:40:25 +05:00
Abdullah Atta
72aa5eccb7 server: fix themes pagination 2023-09-05 15:40:25 +05:00
Abdullah Atta
95e0c4b0d6 editor: reduce task list header padding 2023-09-05 15:40:25 +05:00
Abdullah Atta
9ec6ca0ded web: fix various colors 2023-09-05 15:40:25 +05:00
Abdullah Atta
85b7611214 theme: increase opacity of shade color 2023-09-05 15:40:25 +05:00
Abdullah Atta
d256d435e4 theme: mark shade and textSelection colors as deprecated 2023-09-05 15:40:25 +05:00
Abdullah Atta
577a976e01 theme: derive shade color from accent color 2023-09-05 15:40:25 +05:00
Abdullah Atta
07453834b0 docs: update list of colors in theme-engine docs 2023-09-05 15:40:25 +05:00
Abdullah Atta
59f6b5e411 theme: get rid of shade & textSelection colors 2023-09-05 15:40:25 +05:00
Abdullah Atta
ac9be8360a web: list item title should use heading color 2023-09-05 15:40:25 +05:00
Abdullah Atta
32d0b07f09 web: use accentForeground color instead of white 2023-09-05 15:40:25 +05:00
Abdullah Atta
7b57639076 theme: add new accentForeground color 2023-09-05 15:40:25 +05:00
Abdullah Atta
1fa174bab7 theme: fix input placeholder color 2023-09-05 15:40:25 +05:00
Abdullah Atta
6c52b5a77d editor: editor toolbar separators should use separator color 2023-09-05 15:40:25 +05:00
Abdullah Atta
d4a6157ed3 web: navigation menu separators should use separator color 2023-09-05 15:40:25 +05:00
Abdullah Atta
a86358bbcd web: borders between panes should use scoped border color 2023-09-05 15:40:25 +05:00
Abdullah Atta
20d0e6d047 web: fix menu borders should use border color 2023-09-05 15:40:25 +05:00
Muhammad Ali
51fc4bf3e4 core: add support for aligning images in PDF/HTML exports (#3197)
* web: added function for aligning images

* web: removed regex and added replaceAll in loop

* Update apps/web/src/common/export.ts

Signed-off-by: Abdullah Atta <thecodrr@protonmail.com>

* core: add support for image alignment in html/pdf export

---------

Signed-off-by: Abdullah Atta <thecodrr@protonmail.com>
Co-authored-by: Abdullah Atta <abdullahatta@streetwriters.co>
2023-09-05 15:39:59 +05:00
Abdullah Atta
61d0dafd21 desktop: update app-builder-lib patch 2023-09-04 16:01:04 +05:00
Abdullah Atta
89398cb5e5 misc: remove all mention of crypto-worker 2023-09-04 15:58:38 +05:00
Abdullah Atta
72ec4063de global: update lockfiles 2023-09-04 15:58:23 +05:00
Abdullah Atta
f8423eeea8 editor: update test snapshots 2023-09-04 15:51:47 +05:00
Abdullah Atta
d91203990e editor: respect text direction when exiting node using arrow keys 2023-09-04 15:51:47 +05:00
Abdullah Atta
fc84c1af51 editor: add keyboard specific android editor tests 2023-09-04 15:51:47 +05:00
Abdullah Atta
58de07073d editor: improve escaping from & deleting of nodes 2023-09-04 15:51:47 +05:00
Abdullah Atta
cafa5ff821 editor: fix blockquote padding & margin on mobile 2023-09-04 15:51:47 +05:00
Abdullah Atta
1859799ca3 editor: fix crash on adding a task list 2023-09-04 15:51:47 +05:00
Abdullah Atta
271aa11f74 editor: use list-keymap extension instead of own workarounds 2023-09-04 15:51:47 +05:00
Abdullah Atta
e42f1a65f5 editor: update tiptap packages to v2.1.7 2023-09-04 15:51:47 +05:00
Abdullah Atta
73f3a31614 editor: fix sub outline lists not getting outdented 2023-09-04 15:51:47 +05:00
Abdullah Atta
4d7792d06a editor: fix alignment of outline list indicator 2023-09-04 15:51:47 +05:00
ammarahm-ed
1a0f17c059 mobile: update relations store on open notification 2023-09-04 15:46:37 +05:00
ammarahm-ed
89d941401a mobile: fix beta tags ui 2023-09-04 15:46:23 +05:00
ammarahm-ed
a076ee640d mobile: save report issue information if app gets
restarted or accidentally closed
2023-09-04 15:45:30 +05:00
Abdullah Atta
8c1629f7dd web: ask for password when doing sensitive actions 2023-09-04 15:10:18 +05:00
ammarahm-ed
9a31c07ac5 mobile: add verification prompts 2023-09-04 15:10:18 +05:00
ammarahm-ed
11f39316b5 mobile: fix color scheme 2023-09-04 15:09:03 +05:00
ammarahm-ed
f157229e00 mobile: fix theme gets reset on opening share extension 2023-09-04 15:09:03 +05:00
ammarahm-ed
a0f25c4e6f mobile: fix list not updating on change date sort 2023-09-04 15:07:33 +05:00
Ammar Ahmed
003b3d2a84 Remove hover effect on mobile in editor (#3163)
* editor: remove background on hover on mobile

* editor: fix task item alignment and size on mobile
2023-09-04 15:06:41 +05:00
ammarahm-ed
1775e0bda1 mobile: fix flash on launch on ios 2023-09-04 14:37:24 +05:00
Ammar Ahmed
d5038ac631 mobile: fix quick notes from notifications (#3165)
- Add note to default notebook
- Respect paragraph spacing

Signed-off-by: Ammar Ahmed <40239442+ammarahm-ed@users.noreply.github.com>
2023-09-04 14:36:55 +05:00
ammarahm-ed
7eab77ba01 mobile: fix share sheet sorting 2023-09-04 14:34:53 +05:00
ammarahm-ed
89413c56e2 mobile: auto update pinned note in notif 2023-09-04 14:33:39 +05:00
Muhammad Ali
4d89643203 web: fix multi-select context menu for list items (#3159) 2023-09-04 14:31:00 +05:00
ammarahm-ed
e119590249 mobile: prevent login/signup request when loading 2023-09-04 14:27:35 +05:00
ammarahm-ed
ace2dfac30 core: increase server timeout 2023-09-04 14:27:35 +05:00
Abdullah Atta
5843df0ce8 web: allow encrypted backups for basic users 2023-09-04 14:24:46 +05:00
Abdullah Atta
403ed8adb6 web: do not clear attachment upload status on complete 2023-09-04 14:24:24 +05:00
Abdullah Atta
1eb5c8bc46 core: do not trigger auto sync on settings merge 2023-09-04 14:24:24 +05:00
Abdullah Atta
46f7ec5e00 core: do not trigger autosync on attachment upload 2023-09-04 14:24:24 +05:00
Abdullah Atta
52adece147 web: upload arraybuffer on desktop builds for attachments 2023-09-04 14:24:24 +05:00
Abdullah Atta
de50be9d1b desktop: use net.fetch instead when handling protocol 2023-09-04 14:24:24 +05:00
Abdullah Atta
0f9e32b49f desktop: update electron to 26.1.0 2023-09-04 14:24:24 +05:00
Muhammad Ali
b4c15adf5d editor: fix formatting issues when pasting from ChatGPT (#3217)
Co-authored-by: Abdullah Atta <abdullahatta@streetwriters.co>
2023-09-04 14:22:45 +05:00
Abdullah Atta
a3d3886c51 editor: refactor clipboard extension 2023-09-04 14:22:45 +05:00
Abdullah Atta
0cfa7ce774 editor: paste single line of code as inline code 2023-09-04 14:22:45 +05:00
Abdullah Atta
8299ea909e editor: improve code block detection when pasting from vscode 2023-09-04 14:22:45 +05:00
Abdullah Atta
8b056fe610 editor: preserve formatting when pasting text 2023-09-04 14:22:45 +05:00
Abdullah Atta
511c40db5c editor: do not replace newlines with br when pasting 2023-09-04 14:22:45 +05:00
Muhammad Ali
42d0d42188 web: scroll to top when navigating within settings (#3223) 2023-09-04 13:43:07 +05:00
Abdullah Atta
c80a998444 editor: fix image shuffling on real-time editor sync 2023-09-04 13:41:32 +05:00
ammarahm-ed
0a10df75d1 mobile: add encryptMulti 2023-09-04 13:16:22 +05:00
Abdullah Atta
40d755d191 core: fix all tests 2023-09-04 13:16:22 +05:00
Abdullah Atta
0409a610ab core: improve sync status updates 2023-09-04 13:16:22 +05:00
Abdullah Atta
aa9cd23cf3 core: add fix for localOnly items getting deleted on sync 2023-09-04 13:16:22 +05:00
Abdullah Atta
12d89114a4 core: fix tags & colors not resolving on sync 2023-09-04 13:16:22 +05:00
Abdullah Atta
d5b679187a core: send sync completed event udp style 2023-09-04 13:16:22 +05:00
ammarahm-ed
463ba3754a mobile: add encryptMulti function 2023-09-04 13:16:22 +05:00
Abdullah Atta
2fa8fd14e7 core: migrate to sync v2 for data uploads 2023-09-04 13:16:22 +05:00
Abdullah Atta
fac788d2a9 crypto: remove crypto-worker & simplify crypto interface 2023-09-04 13:16:22 +05:00
ammarahm-ed
6554f900d7 mobile: do not start sync if sync is running 2023-09-04 13:16:22 +05:00
Abdullah Atta
81d3ec83d9 core: fix realtime sync 2023-09-04 13:16:22 +05:00
Abdullah Atta
339c5a357b core: update note-storage mock for tests 2023-09-04 13:16:22 +05:00
ammarahm-ed
b484992037 core: remove console.log 2023-09-04 13:16:22 +05:00
ammarahm-ed
87e42e98c6 mobile: fix logout page ui 2023-09-04 13:16:22 +05:00
ammarahm-ed
51bef01609 mobile: fix logout loading ui 2023-09-04 13:16:22 +05:00
ammarahm-ed
6a49244614 core: fix item not loaded on sync 2023-09-04 13:16:22 +05:00
ammarahm-ed
dad2b61a74 mobile: fix crash when logging out user 2023-09-04 13:16:22 +05:00
ammarahm-ed
fabc595f3d mobile: fix storage 2023-09-04 13:16:22 +05:00
Abdullah Atta
a700787cf3 core: make sure all tests are passing 2023-09-04 13:16:22 +05:00
ammarahm-ed
fb26229715 mobile: update libraries 2023-09-04 13:16:22 +05:00
ammarahm-ed
c4f894f8d0 mobile: add async storage & encryption 2023-09-04 13:16:22 +05:00
Abdullah Atta
ddd1c19c64 core: improve merging performance 2023-09-04 13:16:22 +05:00
Abdullah Atta
79cb3bdba4 core: merge vault key on sync 2023-09-04 13:16:22 +05:00
Abdullah Atta
8447c2150b core: improve sync fetch performance 2023-09-04 13:16:22 +05:00
Abdullah Atta
bc465971be web: add support for bulk writes 2023-09-04 13:16:22 +05:00
Abdullah Atta
f8ce23aa11 crypto: add support for bulk decryption 2023-09-04 13:16:22 +05:00
Abdullah Atta
84004c4d73 global: update lockfiles 2023-08-25 14:30:41 +05:00
Muhammad Ali
2b6aa9b89b ui: allow changing menu button icon color (#3170) 2023-08-21 16:38:21 +05:00
ammarahm-ed
54b8a3e789 mobile: refactor to typescript 2023-08-16 10:14:39 +05:00
Abdullah Atta
7ff5f0001c core: tagging an empty note should not create a content item 2023-08-14 13:41:19 +05:00
Abdullah Atta
d384aa9dcf core: use isolated storage for each test for more reliability 2023-08-14 09:54:57 +05:00
Abdullah Atta
1190da7c91 crypto: use buffer from node:buffer 2023-08-14 09:54:57 +05:00
Abdullah Atta
6561791530 setup: remove dist, build & out dirs during cleanup 2023-08-14 09:54:57 +05:00
Abdullah Atta
26e233d3eb global: update lockfiles 2023-08-14 09:54:57 +05:00
Abdullah Atta
0417981538 global: remove maxNodeModuleJsDepth from tsconfig 2023-08-14 09:54:57 +05:00
Abdullah Atta
81fc56937e web: fix syntax errors 2023-08-14 09:54:57 +05:00
Abdullah Atta
cb778d7e35 core: make dateFormat optional 2023-08-14 09:54:57 +05:00
Abdullah Atta
8442b43715 core: don't include tests & unnecessary files during build 2023-08-14 09:54:57 +05:00
Abdullah Atta
28b3306506 core: import isomorphic-fetch 2023-08-14 09:54:57 +05:00
Abdullah Atta
f51f45aeb2 editor: move isomorphic-fetch import 2023-08-14 09:54:57 +05:00
Abdullah Atta
7f1184736c core: use isomorphic-fetch 2023-08-14 09:54:57 +05:00
Abdullah Atta
e4c8e7a0c5 core: download prismjs languages before tests 2023-08-14 09:54:57 +05:00
Abdullah Atta
44e4722de2 global: fix @notesnook/core direct paths 2023-08-14 09:54:57 +05:00
Abdullah Atta
7c2b0c5947 core: start migrating core to typescript & vitest 2023-08-14 09:54:57 +05:00
Abdullah Atta
6ea912ad0e themebuilder: bump version to 1.1.1 2023-08-11 09:54:09 +05:00
Abdullah Atta
124bbb6ac1 web: do not show website link if there's no homepage 2023-08-11 09:53:01 +05:00
Abdullah Atta
30d6345152 theme: make homepage property in themes optional 2023-08-11 09:53:01 +05:00
Abdullah Atta
60ff7b2f97 themebuilder: update themes manually in themebuilder 2023-08-11 09:51:43 +05:00
Abdullah Atta
c6e90142bb themebuilder: bump version to 1.1.0 2023-08-10 07:20:07 +05:00
Abdullah Atta
1289a025a5 web: skip initiation for theme builder 2023-08-10 07:18:49 +05:00
Abdullah Atta
18d0b72785 themebuilder: fix various bugs 2023-08-10 07:18:18 +05:00
Abdullah Atta
0157cf496f themebuilder: add support for loading from pr, url & base64 2023-08-10 07:17:37 +05:00
Abdullah Atta
3450f2d19d setup: support generating flatpak sources from windows 2023-08-09 16:56:29 +05:00
Abdullah Atta
e509c82807 web: bump version to 2.6.1 2023-08-09 16:31:52 +05:00
Abdullah Atta
d5afd6f32e editor: fix recursive loop on loading a code lang that never registered 2023-08-09 16:26:31 +05:00
Abdullah Atta
fb34dd2864 core: do not throw if language grammar is not found on export 2023-08-09 16:26:31 +05:00
Abdullah Atta
0cd1ce3431 core: don't use require in desktop app 2023-08-09 16:26:31 +05:00
Abdullah Atta
a24447d569 web: fix unstyled "0" in theme details dialog 2023-08-09 16:25:43 +05:00
Abdullah Atta
0bf26afa33 web: fix error on installing theme from file 2023-08-09 16:25:43 +05:00
Abdullah Atta
49455e8054 docs: add documentation for custom themes (#3118)
* themebuilder: add docs

* themebuilder: update theme docs

* docs: finalize docs for custom themes

---------

Co-authored-by: ammarahm-ed <ammarahmed6506@gmail.com>
2023-08-09 15:49:33 +05:00
Abdullah Atta
1136e43115 themebuilder: fix layout 2023-08-08 13:54:56 +05:00
Abdullah Atta
4b74cba4e4 themebuilder: fix builder layout with app on the side 2023-08-08 13:42:28 +05:00
Abdullah Atta
c8a77934f2 ci: use themebuilder scope during bootstrap 2023-08-08 13:26:34 +05:00
Abdullah Atta
e1739e04d1 themebuilder: adjust app branding 2023-08-08 13:19:29 +05:00
Abdullah Atta
a5d39a18a7 web: allow custom app title 2023-08-08 13:19:29 +05:00
Abdullah Atta
77eb661b0e themebuilder: update theme builder on theme switch 2023-08-08 13:19:29 +05:00
Abdullah Atta
667d0d16ea themebuilder: finalize 2023-08-08 13:19:29 +05:00
Abdullah Atta
445ea660c3 web: make web app embeddable 2023-08-08 13:19:29 +05:00
ammarahm-ed
7a35640739 themebuilder: add basic search/replace 2023-08-08 13:19:29 +05:00
ammarahm-ed
f1650fd383 themebuilder: fix accordion 2023-08-08 13:19:29 +05:00
ammarahm-ed
5d67a554d6 themebuilder: fix screenshot 2023-08-08 13:19:29 +05:00
ammarahm-ed
380bfba776 theme: add theme builder app 2023-08-08 13:19:29 +05:00
Abdullah Atta
ed6d125391 clipper: publish v0.3.0 2023-08-08 11:07:22 +05:00
Abdullah Atta
adaa5fd56d clipper: bump version to 0.3.0 2023-08-08 10:29:13 +05:00
Abdullah Atta
0a8db237eb clipper: finalize integration with new theming system 2023-08-08 10:29:13 +05:00
ammarahm-ed
6d723e022b mobile: release v2.6.1 2023-08-08 00:49:29 +05:00
ammarahm-ed
dc3b739719 mobile: fix theme reset on app restart 2023-08-08 00:48:28 +05:00
Abdullah Atta
8d67599278 ci: deploy directly using wrangler 2023-08-07 20:11:11 +05:00
Abdullah Atta
c8da5770c8 web: add list of new features 2023-08-07 14:55:00 +05:00
ammarahm-ed
51c11d9d1a mobile: update release notes 2023-08-07 14:52:28 +05:00
ammarahm-ed
414c164100 mobile: enable remove console.log in release 2023-08-07 14:45:10 +05:00
ammarahm-ed
aa5fd6d946 mobile: release v2.6.0 2023-08-07 14:42:12 +05:00
ammarahm-ed
c89f74d35b mobile: fix ui and cleanup 2023-08-07 14:42:12 +05:00
ammarahm-ed
8c2fdf8554 mobile: fix share extension on ios 2023-08-07 14:42:12 +05:00
ammarahm-ed
f2438d9524 mobile: fix loading local theme 2023-08-07 14:42:12 +05:00
ammarahm-ed
54e7b29716 mobile: update fonts 2023-08-07 14:42:12 +05:00
Abdullah Atta
1d7f4036ec theme: do not send scopes when getting all themes 2023-08-07 12:39:36 +05:00
Abdullah Atta
a2d5eda538 web: fix applied theme status not showing on themes 2023-08-07 12:32:52 +05:00
Abdullah Atta
1b6a27376c web: fix editor & toolbar alignment 2023-08-07 12:00:15 +05:00
Abdullah Atta
8431150f0a web: always show currently applied themes in list 2023-08-07 12:00:02 +05:00
ammarahm-ed
ed5107afaa misc: remove notesnook-themes pulled repo 2023-08-07 09:51:10 +05:00
Abdullah Atta
e52bdd7ca5 web: disable sourcemaps even in testing 2023-08-07 09:18:13 +05:00
Abdullah Atta
26e50848da web: set root to src in vitejs for more focused builds 2023-08-07 09:17:04 +05:00
Abdullah Atta
41fa52c9d5 web: adapt dialog announcements to the new theme 2023-08-07 09:02:26 +05:00
Abdullah Atta
885aa3035e editor: show keyboard shortcuts for tools where possible 2023-08-07 08:47:11 +05:00
Abdullah Atta
b20c20983a ui: improve ui for modifiers in menu items 2023-08-07 08:46:45 +05:00
Abdullah Atta
42994847e9 editor: counter in toolbar should have 100% height 2023-08-07 08:46:13 +05:00
Abdullah Atta
b59a12e743 ui: simplify sub-menus markup 2023-08-07 08:45:18 +05:00
Abdullah Atta
f67792cd30 editor: use error color for errors in math blocks 2023-08-07 08:44:45 +05:00
Abdullah Atta
60ba345e6c editor: add font-display: swap to hack fontface 2023-08-07 08:44:27 +05:00
Abdullah Atta
0074548b84 web: fix broken colors in some places 2023-08-07 06:54:33 +05:00
Abdullah Atta
5af5549173 web: introduce "dangerous" variant for menu items 2023-08-07 06:54:11 +05:00
Abdullah Atta
f9f0235d7b editor: replace all old css colors with new ones 2023-08-07 06:03:25 +05:00
Abdullah Atta
cd51420931 web: replace all old css colors with new ones 2023-08-07 06:03:11 +05:00
Abdullah Atta
000d11976b web: update playwright to 1.36.2 2023-08-07 05:49:02 +05:00
Abdullah Atta
392261c208 web: add helper to imperitively match a media query 2023-08-07 05:49:02 +05:00
Abdullah Atta
71aee40f8d web: improve backups test reliability 2023-08-07 05:49:02 +05:00
Abdullah Atta
565ef2c321 web: don't check for notification permission in tests 2023-08-07 05:49:02 +05:00
Abdullah Atta
2d5c8c9e6d web: only allow changing title via event 2023-08-07 05:49:02 +05:00
Abdullah Atta
800f983688 web: show progress when restoring encrypted backups 2023-08-07 05:49:02 +05:00
ammarahm-ed
7291e54db7 mobile: update detox to latest 2023-08-05 16:46:26 +05:00
ammarahm-ed
794b1c70eb mobile: fix editor stuck in loading state 2023-08-05 16:46:26 +05:00
Abdullah Atta
330b227027 web: improve real time sync test reliability 2023-08-05 16:27:42 +05:00
Abdullah Atta
59a66a63c5 web: update all checkout snapshots 2023-08-05 13:52:19 +05:00
Abdullah Atta
ceb28f08c5 web: load paddle checkout in test mode during testing 2023-08-05 12:27:10 +05:00
Abdullah Atta
7287754218 web: bump version to 2.6.0 2023-08-05 12:19:43 +05:00
Abdullah Atta
80d2f64f10 desktop: bring app to front on start of 2nd instance 2023-08-05 12:10:47 +05:00
Abdullah Atta
9951b83539 web: fix navigation menu resizing on tablet mode 2023-08-05 12:01:06 +05:00
Abdullah Atta
66d50be35a theme: improve check for inheriting emotion theme 2023-08-05 11:27:56 +05:00
Abdullah Atta
9eb5a4cee4 web: update all test screenshot snapshots for linux 2023-08-05 11:20:08 +05:00
Abdullah Atta
3c575c01ad web: fix storage for Firefox & Tor private mode 2023-08-05 11:16:18 +05:00
ammarahm-ed
643028f5d8 mobile: update deps 2023-08-05 11:15:06 +05:00
ammarahm-ed
3831b9409e editor: load theme from local storage on init 2023-08-05 11:15:06 +05:00
ammarahm-ed
985ae848d3 mobile: fix tags wrapping in share extension 2023-08-05 11:15:06 +05:00
Abdullah Atta
0c8c8b6040 editor: fix sub toolbar not getting aligned correctly 2023-08-05 11:15:06 +05:00
ammarahm-ed
a01c084045 mobile: fix react-native-html-to-pdf-lite 2023-08-05 11:15:06 +05:00
ammarahm-ed
7577adf09d mobile: ensure that font size is always number 2023-08-05 11:15:06 +05:00
Abdullah Atta
394002eb4c editor(mobile): add theme to global styles 2023-08-05 11:15:06 +05:00
Abdullah Atta
5c386148a4 editor: fix action sheets not opening 2023-08-05 11:15:06 +05:00
Abdullah Atta
3558d3db93 editor: do not try to download image if src is invalid 2023-08-05 11:15:06 +05:00
ammarahm-ed
2bed812df1 mobile: fix editor 2023-08-05 11:15:06 +05:00
ammarahm-ed
c5db2e0ea9 mobile: fix content in share ext editor takes too long to load 2023-08-05 11:15:06 +05:00
ammarahm-ed
6cd1d7c41b mobile: fix webpack build 2023-08-05 11:15:06 +05:00
ammarahm-ed
36662d58da mobile: fix editor when app enter foreground from share ext 2023-08-05 11:15:06 +05:00
ammarahm-ed
e85cc6ac56 mobile: fix ext height behind keyboard on mobile 2023-08-05 11:15:06 +05:00
ammarahm-ed
a17d2d69ef mobile: fix opening search causes crash in share ext 2023-08-05 11:15:06 +05:00
Abdullah Atta
3ec615a6ec web: update export html snapshot for linux 2023-08-05 10:19:48 +05:00
Abdullah Atta
8eadc6aea1 web: do not redirect automatically on login session expire 2023-08-05 09:50:47 +05:00
Abdullah Atta
506678e72c web: format reminder time correctly (fixes #3085) 2023-08-04 20:05:53 +05:00
Abdullah Atta
aa3561555e desktop: fix desktop toggle settings 2023-08-04 19:34:46 +05:00
Abdullah Atta
405a8aaad0 editor: fix embeds in readonly mode are not visible 2023-08-04 19:33:57 +05:00
Abdullah Atta
7edd45419e editor: align outline bullet in the center of item 2023-08-04 19:00:54 +05:00
Abdullah Atta
5af028698c editor: only put bullet before 1st p tag in outline list 2023-08-04 18:27:04 +05:00
Abdullah Atta
ce822e7538 web: fix toggle colors 2023-08-04 16:09:29 +05:00
Abdullah Atta
9936bee8e0 editor: copying 1 nested list item shouldn't copy list meta 2023-08-04 16:07:31 +05:00
Abdullah Atta
b903e8461d web: make monograph password field hidden 2023-08-04 12:58:05 +05:00
ammarahm-ed
4d2c42330e mobile: fix crash on opening sheet with icon 2023-08-04 12:10:58 +05:00
Abdullah Atta
0ea37fdb31 web: improve checkout responsiveness 2023-08-04 10:23:36 +05:00
Abdullah Atta
22bf738d21 web: make sure all tests are passing 2023-08-03 18:48:51 +05:00
Abdullah Atta
922ba7d192 web: fix errored attachment icon color 2023-08-03 15:55:54 +05:00
Abdullah Atta
c38a98c239 web: upload files < 25mb in a single request 2023-08-03 15:55:54 +05:00
Abdullah Atta
0f9452f6ca web: improve handling of errors during upload 2023-08-03 15:55:54 +05:00
Abdullah Atta
5cc9c6e3e4 web: change themes api url to themes-api.notesnook.com 2023-08-03 15:48:42 +05:00
Abdullah Atta
5da30faf71 web: use settings scroller for themes 2023-08-03 15:46:15 +05:00
ammarahm-ed
99d984f516 core: flush once every 10 sec 2023-08-03 15:02:37 +05:00
ammarahm-ed
20f0760f13 core: fix logger performance 2023-08-03 15:02:37 +05:00
ammarahm-ed
20ffa9bee5 mobile: use themes-api server 2023-08-03 13:58:40 +05:00
Abdullah Atta
bdd003f177 web: fix colors in various places 2023-08-03 13:21:59 +05:00
Abdullah Atta
a45a960bfd editor(mobile): do not override background color 2023-08-03 13:21:59 +05:00
ammarahm-ed
fbf56f2663 mobile: fix incorrect colors uage 2023-08-03 12:01:11 +05:00
ammarahm-ed
0e7ac37b9d mobile: remove prism.css 2023-08-03 11:18:22 +05:00
Abdullah Atta
bcf0d86605 web: use new error icon color 2023-08-03 04:12:08 +05:00
Abdullah Atta
954f894738 web: fix crash when opening attachment menu 2023-08-03 01:44:21 +05:00
Abdullah Atta
908def1b69 editor: update lockfile 2023-08-02 19:50:41 +05:00
Abdullah Atta
f26f638ad4 editor: fix action sheet not closing via drag 2023-08-02 19:50:41 +05:00
Abdullah Atta
700f47fd49 web: disable overscroll behavior 2023-08-02 19:50:41 +05:00
Abdullah Atta
3d0eaacaac theme: allow passing ref to emotion theme provider 2023-08-02 19:50:40 +05:00
Abdullah Atta
c76118d9d6 web: load full framer-motion 2023-08-02 19:50:40 +05:00
Abdullah Atta
148c39158b editor: fix crash on action sheet opening 2023-08-02 19:50:40 +05:00
Ammar Ahmed
89c2d45a4b mobile: reduce app size (#3068)
* mobile: reduce app size

* editor: substitute all @mdi/js icons at build time

* mobile: add script to tree shake icon font file

* mobile: fix icon loading

* mobile: remove html-entities dep

* mobile: add missing icon fonts

* mobile: include plain editor

* mobile: add missing fonts

* mobile: use webpack-bundle

* mobile: keep generated fonts in repo

* mobile: update fonts

* mobile: fix duplicate key warning

* mobile: update fonts

* mobile: disable gesure on reminder sheet

* mobile: update fonts

* mobile: reset session correctly on logout

* mobile: update icon fonts

* mobile: set button action on reminder sheet

* mobile: add missing icons

* mobile: fix crash

* mobile: fix right menus

* mobile: remove console.log

* mobile: disable bounce effect

* mobile: update deps

---------

Co-authored-by: Abdullah Atta <abdullahatta@streetwriters.co>
2023-08-02 15:35:15 +05:00
Muhammad Ali
c30c54ab97 web: fix cursor jumps on edit in sticky title (#3028)
Co-authored-by: Abdullah Atta <abdullahatta@streetwriters.co>
2023-08-02 15:18:52 +05:00
Abdullah Atta
734cfba45d ci: publish preview on PR open 2023-08-02 15:04:27 +05:00
Abdullah Atta
3b363a1b20 web: prevent scroll jump when toggling focus mode 2023-08-02 15:04:27 +05:00
Abdullah Atta
a747bafa69 web: use new editorSidebar 2023-08-02 13:10:23 +05:00
Abdullah Atta
9d0c11b051 web: unlock view should use base theme scope 2023-08-02 13:09:56 +05:00
Abdullah Atta
bd9e997ef1 theme: add new editorSidebar scope 2023-08-02 13:09:30 +05:00
Abdullah Atta
f222327a89 theme: fix menu buttons have no hover 2023-08-02 13:09:09 +05:00
Abdullah Atta
4e5351134f global: update lockfiles 2023-08-02 12:26:35 +05:00
Abdullah Atta
ec1d5cc211 setup: only grab local deps during bootstrap 2023-08-02 12:26:22 +05:00
Abdullah Atta
8f41e25ac1 ci: ignore paths unrelated to web app 2023-08-02 12:12:32 +05:00
Abdullah Atta
2d90caafc6 ci: use unlike-ltd/github-actions-cloudflare-pages for deployment 2023-08-02 12:12:32 +05:00
Abdullah Atta
a9f36076b3 ci: deploy master branch to cloudflare as beta 2023-08-02 11:38:35 +05:00
Abdullah Atta
8365e557d5 editor: do not copy prism-theme during build 2023-08-02 11:37:15 +05:00
Abdullah Atta
080160ca70 vericrypt: use old @notesnook/theme version 2023-08-02 11:28:54 +05:00
Abdullah Atta
e6164b0b3e setup: use 4 threads at the minimum when bootstrapping 2023-08-01 20:45:08 +05:00
Abdullah Atta
26d786a368 editor: fix codeblock tests 2023-08-01 20:45:08 +05:00
Abdullah Atta
c6172644f0 editor: DOMSerializer2 -> ClipboardDOMSerializer 2023-08-01 20:45:08 +05:00
Abdullah Atta
5810947a5d editor: improve copy pasting 2023-08-01 20:45:08 +05:00
Abdullah Atta
708c2d373d editor: remove unnecessary console.logs 2023-08-01 20:45:08 +05:00
Abdullah Atta
d11a9b2219 editor: fix tests 2023-08-01 20:39:16 +05:00
Abdullah Atta
32b8b008f2 web: make export tests pass 2023-08-01 20:39:16 +05:00
Abdullah Atta
e066d1ce8d global: update lockfiles 2023-08-01 20:39:16 +05:00
Abdullah Atta
4f3f19a785 web: extract editor styles into a fixed css file during build 2023-08-01 20:39:16 +05:00
Abdullah Atta
6c3ea4517d core: inline codeblock styles into html template 2023-08-01 20:39:16 +05:00
Abdullah Atta
1e7e3b8c42 core: use explicit import/require for dynamic imports 2023-08-01 20:39:16 +05:00
Abdullah Atta
a54c18e89b web: remove require transform plugin 2023-08-01 20:39:16 +05:00
Abdullah Atta
ed4d5efea6 editor: generate code languages index during build 2023-08-01 20:39:16 +05:00
Abdullah Atta
4bab279345 global: fix conflicting package versions across subprojects 2023-08-01 20:39:16 +05:00
ammarahm-ed
ec32eeba0b mobile: fix pdf exports with codeblock & katex 2023-08-01 20:39:16 +05:00
Abdullah Atta
2b74e2afb2 web: fix Dynamic module import is disabled error 2023-08-01 20:33:37 +05:00
Abdullah Atta
c4a9d244a9 editor: bump katex version to 0.16.4 2023-08-01 18:35:31 +05:00
Abdullah Atta
6643fb0d63 web: fix old usage of hooks/useTheme 2023-08-01 18:32:58 +05:00
Abdullah Atta
b2a3949ad0 web: auto detect text direction where possible 2023-08-01 15:48:52 +05:00
Abdullah Atta
93c1a44285 editor: infer text direction from parent before insertion 2023-08-01 15:48:52 +05:00
Abdullah Atta
62c4fb328c editor: use single input rule for heading 2023-08-01 15:48:52 +05:00
Muhammad Ali
578fb72ddc editor: infer dir from parent when adding heading 2023-08-01 15:48:52 +05:00
Abdullah Atta
19faaea10e editor: do not save note with codeblock on open 2023-08-01 12:36:41 +05:00
Muhammad Ali
9951aae398 web: use current color scheme in all fields (#3039)
Co-authored-by: Abdullah Atta <abdullahatta@streetwriters.co>
2023-08-01 12:32:36 +05:00
Abdullah Atta
0963559a5f global: update lockfiles 2023-08-01 12:09:10 +05:00
Ammar Ahmed
622294b807 global: implement the new theme engine (#2196)
* mobile: theme

* theme: add theme engine

* mobile: migrate app colors to new theme engine

* mobile: fixed some colors

* mobile: fix colors

* mobile: store theme info in store

* theme: `ColorsType` -> `Variants`

* theme: use explicit return type for `useThemeColors`

* theme: add `backdrop` color

* mobile: `const colors` -> `const {colors}

* theme: add default pitch-black theme

* mobile: manage theme state via theme-engine

* mobile: add theme scopes

* mobile: commit

* mobile: fix button width on applock screen

* mobile: fix typings

* mobile: fix theme definition

* web: add partial support for custom themes

only context menus & popups are left.

* theme: add dialog & sheet scopes

* global: sync with master branch and make everything work again

* mobile: fix theme-engine usage in editor & app

* mobile: fix colors

* mobile: fix colors

* mobile: cleanup

* mobile: fix status bar color incorrect on entering foreground

* mobile: fix dark color scheme

* web: move emotion theme provider to @notesnook/theme

* editor: add support for theme enging

* web: adjust hover & focus colors on list item

* mobile: migrate share ext to theme engine

* mobile: fix editor theme provider

* clipper: add support for the new theme engine

* mobile: fix statusbar color on switch from bg

* misc: fix build

* mobile: fix build

* misc: fix colors

* mobile: fix theme colors

* mobile: fix bottom padding

* server: add theme server

* theme: add previewColors

* server: support themes query pagination

* mobile: add client from theme server

* server: reset cache on sync repo

* server: fix types

* server: show ip & port on start server

* server: theme updates

* web: finalize new theme engine on web

* editor: fix build

* global: fix @emotion/react version to 11.11.1

* editor: update katex patch

* web: fix imports

* global: fix @trpc/* versions

* global: a huge set of changes

1. get rid of ThemeVariant. All variants can now be accessed anywhere.
2. remove unnecessary button variants
3. make buttons more responsive
4. implement themes server

* web: add support for theme search and theme switching

* global: update lockfiles

* mobile: fix error

* theme: use vite-plugin-react to start theme server

* web: add support for auto updating themes

* mobile: update theme selector

* mobile: update theme if new verison available

* theme: add `isomorphic-fetch` package

* global: update lockfiles

* web: add theme details dialog

* setup: add scope for themes server in bootstrap script

* web: add production server url

* web: update lockfile

* web: update lockfile

* mobile: remove `react-native-blob-util`

* web: add support for endless scrolling in themes

* web: bring back dark/light mode option in settings

* web: fix colors in places

* theme: add selected variant

* global: use single typescript version across the projects

* web: fix sort & group options not having submenus

* web: apply selected variant where appropriate

* ui: use unique id for all menu items

* config: add ui scope for commits

* theme: export button variant creation fn

* web: fix only 1 theme showing in theme selector

* web: fix navigation item hover & other colors

* mobile: update theme

* editor: fix toolbar group alignments

* editor: set theme provider at app level

* theme: use scope name to get current scope

* mobile: fix color usage in message card

* theme: remove caching

* editor: bring back icons in table menus

* theme: use zustand to manage theme engine state

* web: fix login/signup theming

* mobile: fix webpack build

* misc: remove ThemeProvider usage

* editor: adjust theming and styling of editor toolbar

* mobile: refactor

* editor: fix toolbar group padding everywhere

* web: fix settings sidebar is not scrollable

* web: add loading indicator for themes loading

* mobile: fix warning

* mobile: fix ui issues

* web: fix Loader errors on build

* theme: add getPreviewColors & validateTheme

* theme: fix theme validation

* mobile: load theme from file

* mobile: fix share extension crash

* mobile: rename state

* theme: add sourceURL property

* theme: refactor theme-engine

* web: add support for loading theme from file

* web: improve button hover interaction

* mobile: fix floating button color

* mobile: update theme

* mobile: fix border radius of context menu

* mobile: set sheet overlay color to theme backdrop

* mobile: set sidemenu backdrop to theme backdrop

---------

Co-authored-by: Abdullah Atta <abdullahatta@streetwriters.co>
2023-08-01 12:07:21 +05:00
Abdullah Atta
ba72a92a8a setup: switch to fdir from fast-glob 2023-08-01 10:42:21 +05:00
Abdullah Atta
5a5e072fb4 global: update all lockfiles 2023-08-01 10:42:07 +05:00
Abdullah Atta
91cb2935eb logger: publish to npm 2023-08-01 09:33:45 +05:00
Abdullah Atta
387aeab319 core: bump version to 7.4.1 2023-08-01 09:33:28 +05:00
Abdullah Atta
4db7b54a5f theme: bump version to 1.2.0 2023-08-01 09:32:23 +05:00
Abdullah Atta
ddfd7254f1 crypto: publish v1 of @notesnook/sodium 2023-08-01 09:32:09 +05:00
Abdullah Atta
2e410c521a crypto: bump version to 1.1.1 2023-08-01 09:31:45 +05:00
Abdullah Atta
5dd22e85fa editor: bump version to 1.6.1 2023-08-01 09:31:06 +05:00
Abdullah Atta
022dd78eda ci: add workflow for vericrypt publishing 2023-07-31 11:40:09 +05:00
Abdullah Atta
2b1812b5de vericrypt: initial commit 2023-07-31 11:40:09 +05:00
Abdullah Atta
3c9678c6d5 core: reduce sync logger verbosity 2023-07-27 17:12:58 +05:00
Abdullah Atta
4f1f9ecb6b docs: make first step clear as; go to importer.notesnook.com 2023-07-26 13:34:20 +05:00
Abdullah Atta
07fb572a67 core: fix html export empty p tag regex 2023-07-26 12:08:51 +05:00
Abdullah Atta
7d7f4acc8c clipper: remove unnecessary packages 2023-07-25 12:35:31 +05:00
Abdullah Atta
efba05291d global: update lockfiles 2023-07-25 12:31:41 +05:00
Abdullah Atta
8b0d7a5cdb web: include __tests__ directory in tsconfig 2023-07-25 12:28:54 +05:00
Abdullah Atta
162deccf04 web: remove unused renderMath function 2023-07-25 12:28:54 +05:00
alihamuh
083f14bf50 editor: use dom parser to convert br to p tags 2023-07-25 12:28:54 +05:00
Abdullah Atta
cae3f7354e web: convert require to await import 2023-07-25 08:09:27 +05:00
Muhammad Ali
75e62a3fac core: pre-render math & code blocks before HTML export 2023-07-25 08:09:27 +05:00
Abdullah Atta
69dee3be78 web: get rid of print-js lib for printing 2023-07-24 15:50:36 +05:00
Abdullah Atta
36bf7c5cc6 web: don't show "Failed to unlock vault" error when exporting 1 note 2023-07-24 15:50:36 +05:00
Muhammad Ali
c041819930 web: add &nbsp; in empty p tags for printing 2023-07-24 15:50:36 +05:00
Muhammad Ali
dd4d00eae6 web: move focus to editor on Tab press in note title (#2983)
Co-authored-by: Abdullah Atta <abdullahatta@streetwriters.co>
2023-07-24 14:33:00 +05:00
1440 changed files with 367190 additions and 170057 deletions

View File

@@ -14,13 +14,16 @@ const SCOPES = [
"mobile",
"web",
"vericrypt",
"desktop",
"crypto",
"editor",
"logger",
"theme",
"server",
"core",
"fs",
"ui",
"clipper",
"config",
"ci",
@@ -30,7 +33,8 @@ const SCOPES = [
"misc",
"common",
"global",
"docs"
"docs",
"themebuilder"
];
module.exports = {

View File

@@ -15,6 +15,8 @@ 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/

View File

@@ -78,7 +78,13 @@ module.exports = {
semi: ["error", "always"],
"@typescript-eslint/no-empty-function": "off",
"react/prop-types": "off",
"header/header": ["error", "block", LICENSE, 1]
"header/header": ["error", "block", LICENSE, 1],
"@typescript-eslint/no-empty-interface": [
"error",
{
allowSingleExtends: true
}
]
},
settings: {
react: {

View File

@@ -5,9 +5,9 @@ runs:
using: "composite"
steps:
- name: Setup Node
uses: actions/setup-node@v3
uses: actions/setup-node@v4
with:
node-version: 16.x
node-version: 20.x
cache: "npm"
cache-dependency-path: |
package-lock.json
@@ -19,7 +19,6 @@ runs:
packages/crypto/package-lock.json
packages/sodium/package-lock.json
packages/clipper/package-lock.json
packages/crypto-worker/package-lock.json
packages/editor-mobile/package-lock.json
packages/editor/package-lock.json
packages/logger/package-lock.json

View File

@@ -6,6 +6,11 @@ jobs:
build:
runs-on: ubuntu-latest
timeout-minutes: 40
env:
CMAKE_C_COMPILER_LAUNCHER: ccache
CMAKE_CXX_COMPILER_LAUNCHER: ccache
NDK_CCACHE: ccache
USE_CCACHE: 1
steps:
- name: Checkout
@@ -24,30 +29,126 @@ jobs:
run: |
npm ci --ignore-scripts --prefer-offline --no-audit
npm run bootstrap -- --scope=mobile
- name: Make Gradlew Executable
run: cd apps/mobile/native/android && chmod +x ./gradlew
- name: Install CCache
uses: hendrikmuhs/ccache-action@v1.2.11
with:
key: ${{ runner.os }}-ccache-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
max-size: 1500M
restore-keys: |
${{ runner.os }}-ccache-
- name: Setup CCache
run: |
ln -s $(which ccache) /usr/local/bin/gcc
ln -s $(which ccache) /usr/local/bin/g++
ln -s $(which ccache) /usr/local/bin/cc
ln -s $(which ccache) /usr/local/bin/c++
ln -s $(which ccache) /usr/local/bin/clang
ln -s $(which ccache) /usr/local/bin/clang++
ccache --set-config=base_dir=$PWD
ccache --set-config=inode_cache=true
ccache --set-config=compiler_check=content
ccache --set-config=depend_mode=true
ccache --set-config=file_clone=true
ccache --set-config=sloppiness=clang_index_store,file_stat_matches,include_file_ctime,include_file_mtime,ivfsoverlay,pch_defines,modules,system_headers,time_macros
ccache -p
- name: Cache gradle
uses: actions/cache@v2
with:
path: |
~/.gradle/caches
~/.gradle/wrapper
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
- name: Patch CPP Projects to use CCache
run: |
sed -i -e 's/arguments/arguments "-DCMAKE_CXX_COMPILER_LAUNCHER=ccache",/g' apps/mobile/node_modules/react-native-mmkv-storage/android/build.gradle
sed -i -e 's/arguments/arguments "-DCMAKE_CXX_COMPILER_LAUNCHER=ccache",/g' apps/mobile/node_modules/react-native-reanimated/android/build.gradle
sed -i -e 's/defaultConfig {/ndkVersion safeExtGet('ndkVersion', "25.2.9519653")\n defaultConfig {/g' apps/mobile/node_modules/react-native-reanimated//android/build.gradle
sed -i -e 's/defaultConfig {/ndkVersion safeExtGet('ndkVersion', "25.2.9519653")\n defaultConfig {/g' apps/mobile/node_modules/react-native-mmkv-storage/android/build.gradle
- name: CCache Stats Before Build
run: ccache -sv
- name: Build unsigned app bundle
run: yarn release:android:bundle
- name: CCache Stats after build
run: ccache -sv
- name: Sign app bundle for Playstore release
id: sign_app
uses: r0adkll/sign-android-release@v1
uses: r0adkll/sign-android-release@master
with:
releaseDirectory: apps/mobile/native/android/app/build/outputs/bundle/release
signingKeyBase64: ${{ secrets.SIGNING_KEY }}
alias: ${{ secrets.ALIAS }}
keyStorePassword: ${{ secrets.KEY_PASSWORD }}
keyPassword: ${{ secrets.KEY_PASSWORD }}
env:
BUILD_TOOLS_VERSION: "33.0.0"
- name: Build apks for Github release
run: yarn release:android
- name: Sign apk files
id: sign_apk
uses: r0adkll/sign-android-release@master
with:
releaseDirectory: apps/mobile/native/android/app/build/outputs/apk/release
signingKeyBase64: ${{ secrets.PUBLIC_SIGNING_KEY }}
alias: ${{ secrets.PUBLIC_ALIAS }}
keyStorePassword: ${{ secrets.PUBLIC_KEY_PASSWORD }}
keyPassword: ${{ secrets.PUBLIC_KEY_PASSWORD }}
env:
BUILD_TOOLS_VERSION: "33.0.0"
- name: Rename apk files
run: |
cd apps/mobile/native/android/app/build/outputs/apk/release/
mv app-arm64-v8a-release-unsigned-signed.apk notesnook-arm64-v8a.apk
mv app-armeabi-v7a-release-unsigned-signed.apk notesnook-armeabi-v7a.apk
mv app-x86-release-unsigned-signed.apk notesnook-x86.apk
mv app-x86_64-release-unsigned-signed.apk notesnook-x86_64.apk
- name: Get app version
id: package-version
uses: martinbeentjes/npm-get-version-action@master
with:
path: apps/mobile
- name: Publish to Playstore
id: deploy
uses: r0adkll/upload-google-play@v1
uses: r0adkll/upload-google-play@v1.1.1
with:
serviceAccountJsonPlainText: ${{ secrets.SERVICE_ACCOUNT_JSON }}
packageName: com.streetwriters.notesnook
releaseFile: ${{steps.sign_app.outputs.signedReleaseFile}}
track: internal
releaseFiles: ${{steps.sign_app.outputs.signedReleaseFile}}
track: alpha
status: completed
whatsNewDirectory: apps/mobile/native/android/releasenotes/
- name: Create release draft on Github
uses: softprops/action-gh-release@v1
with:
draft: true
tag_name: ${{ steps.package-version.outputs.current-version}}-beta-android
name: Notesnook Android v${{ steps.package-version.outputs.current-version}} Beta
repository: streetwriters/notesnook
token: ${{ secrets.GITHUB_TOKEN }}
files: |
apps/mobile/native/android/app/build/outputs/apk/release/notesnook-arm64-v8a.apk
apps/mobile/native/android/app/build/outputs/apk/release/notesnook-armeabi-v7a.apk
apps/mobile/native/android/app/build/outputs/apk/release/notesnook-x86.apk
apps/mobile/native/android/app/build/outputs/apk/release/notesnook-x86_64.apk
- name: Upload signed aab to Github
uses: actions/upload-artifact@v2
with:
name: Notesnook.aab
path: ${{steps.sign_app.outputs.signedReleaseFile}}

View File

@@ -1,4 +1,4 @@
name: Publish @notesnook/mobile
name: Publish @notesnook/android
on: workflow_dispatch
@@ -6,6 +6,11 @@ jobs:
build:
runs-on: ubuntu-latest
timeout-minutes: 40
env:
CMAKE_C_COMPILER_LAUNCHER: ccache
CMAKE_CXX_COMPILER_LAUNCHER: ccache
NDK_CCACHE: ccache
USE_CCACHE: 1
steps:
- name: Checkout
@@ -28,29 +33,65 @@ jobs:
- name: Make Gradlew Executable
run: cd apps/mobile/native/android && chmod +x ./gradlew
- name: Install CCache
uses: hendrikmuhs/ccache-action@v1.2.11
with:
key: ${{ runner.os }}-ccache-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
max-size: 1500M
restore-keys: |
${{ runner.os }}-ccache-
- name: Setup CCache
run: |
ln -s $(which ccache) /usr/local/bin/gcc
ln -s $(which ccache) /usr/local/bin/g++
ln -s $(which ccache) /usr/local/bin/cc
ln -s $(which ccache) /usr/local/bin/c++
ln -s $(which ccache) /usr/local/bin/clang
ln -s $(which ccache) /usr/local/bin/clang++
ccache --set-config=base_dir=$PWD
ccache --set-config=inode_cache=true
ccache --set-config=compiler_check=content
ccache --set-config=depend_mode=true
ccache --set-config=file_clone=true
ccache --set-config=sloppiness=clang_index_store,file_stat_matches,include_file_ctime,include_file_mtime,ivfsoverlay,pch_defines,modules,system_headers,time_macros
ccache -p
- name: Cache gradle
uses: actions/cache@v2
with:
path: |
~/.gradle/caches
~/.gradle/wrapper
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
- name: Patch CPP Projects to use CCache
run: |
sed -i -e 's/arguments/arguments "-DCMAKE_CXX_COMPILER_LAUNCHER=ccache",/g' apps/mobile/node_modules/react-native-mmkv-storage/android/build.gradle
sed -i -e 's/arguments/arguments "-DCMAKE_CXX_COMPILER_LAUNCHER=ccache",/g' apps/mobile/node_modules/react-native-reanimated/android/build.gradle
sed -i -e 's/defaultConfig {/ndkVersion safeExtGet('ndkVersion', "25.2.9519653")\n defaultConfig {/g' apps/mobile/node_modules/react-native-reanimated//android/build.gradle
sed -i -e 's/defaultConfig {/ndkVersion safeExtGet('ndkVersion', "25.2.9519653")\n defaultConfig {/g' apps/mobile/node_modules/react-native-mmkv-storage/android/build.gradle
- name: CCache Stats Before Build
run: ccache -sv
- name: Build unsigned app bundle
run: yarn release:android:bundle
- name: CCache Stats after build
run: ccache -sv
- name: Sign app bundle for Playstore release
id: sign_app
uses: r0adkll/sign-android-release@v1
uses: r0adkll/sign-android-release@master
with:
releaseDirectory: apps/mobile/native/android/app/build/outputs/bundle/release
signingKeyBase64: ${{ secrets.SIGNING_KEY }}
alias: ${{ secrets.ALIAS }}
keyStorePassword: ${{ secrets.KEY_PASSWORD }}
keyPassword: ${{ secrets.KEY_PASSWORD }}
- name: Publish to Playstore
id: deploy
uses: r0adkll/upload-google-play@v1.1.1
with:
serviceAccountJsonPlainText: ${{ secrets.SERVICE_ACCOUNT_JSON }}
packageName: com.streetwriters.notesnook
releaseFiles: ${{steps.sign_app.outputs.signedReleaseFile}}
track: production
status: completed
whatsNewDirectory: apps/mobile/native/android/releasenotes/
env:
BUILD_TOOLS_VERSION: "33.0.0"
- name: Build apks for Github release
run: yarn release:android
@@ -64,6 +105,8 @@ jobs:
alias: ${{ secrets.PUBLIC_ALIAS }}
keyStorePassword: ${{ secrets.PUBLIC_KEY_PASSWORD }}
keyPassword: ${{ secrets.PUBLIC_KEY_PASSWORD }}
env:
BUILD_TOOLS_VERSION: "33.0.0"
- name: Rename apk files
run: |
@@ -79,6 +122,18 @@ jobs:
with:
path: apps/mobile
- name: Publish to Playstore
id: deploy
uses: r0adkll/upload-google-play@v1.1.1
with:
serviceAccountJsonPlainText: ${{ secrets.SERVICE_ACCOUNT_JSON }}
packageName: com.streetwriters.notesnook
releaseFiles: ${{steps.sign_app.outputs.signedReleaseFile}}
track: production
status: completed
whatsNewDirectory: apps/mobile/native/android/releasenotes/
- name: Create release draft on Github
uses: softprops/action-gh-release@v1
with:
@@ -92,3 +147,9 @@ jobs:
apps/mobile/native/android/app/build/outputs/apk/release/notesnook-armeabi-v7a.apk
apps/mobile/native/android/app/build/outputs/apk/release/notesnook-x86.apk
apps/mobile/native/android/app/build/outputs/apk/release/notesnook-x86_64.apk
- name: Upload signed aab to Github
uses: actions/upload-artifact@v2
with:
name: Notesnook.aab
path: ${{steps.sign_app.outputs.signedReleaseFile}}

View File

@@ -18,6 +18,21 @@ on:
required: true
default: true
description: "Publish on GitHub releases?"
build-windows:
type: boolean
required: true
default: true
description: "Build for Windows?"
build-linux:
type: boolean
required: true
default: true
description: "Build for Linux?"
build-mac:
type: boolean
required: true
default: true
description: "Build for macOS?"
jobs:
build:
@@ -43,7 +58,7 @@ jobs:
run: npx nx build:desktop @notesnook/web
- name: Archive build artifact
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: build
path: apps/web/build/**/*
@@ -67,11 +82,13 @@ jobs:
name: Notesnook Desktop v${{ steps.app_metadata.outputs.app_version }}
tag_name: v${{ steps.app_metadata.outputs.app_version }}
files: ./notesnook_build_v${{ steps.app_metadata.outputs.app_version }}.zip
prerelease: ${{ endsWith(steps.app_metadata.outputs.app_version, '-beta') }}
build-macos:
name: Build for macOS
needs: build
runs-on: macos-12
if: inputs.build-mac
runs-on: macos-14
steps:
- name: Check out Git repository
@@ -97,6 +114,7 @@ jobs:
- name: Get App Store Version
id: appstore
uses: streetwriters/appstore-connect-app-version@develop
if: inputs.publish-apple
with:
app-id: ${{ steps.app_metadata.outputs.apple_app_id }}
key-id: ${{ secrets.api_key_id }}
@@ -105,7 +123,7 @@ jobs:
platform: "MAC_OS"
- name: Download build
uses: actions/download-artifact@v3
uses: actions/download-artifact@v4
with:
name: build
path: ./apps/web/build
@@ -136,7 +154,7 @@ jobs:
CSC_KEY_PASSWORD: ${{ secrets.mac_certs_password }}
APPLE_API_KEY: ~/private_keys/AuthKey_${{ secrets.api_key_id }}.p8
APPLE_API_KEY_ID: ${{ secrets.api_key_id }}
APPLE_API_KEY_ISSUER: ${{ secrets.api_key_issuer_id }}
APPLE_API_ISSUER: ${{ secrets.api_key_issuer_id }}
run: |
npx nx run release --project @notesnook/desktop
if [ ${{ inputs.publish-github }} == true ]; then
@@ -162,6 +180,7 @@ jobs:
build-linux:
name: Build for Linux
needs: build
if: inputs.build-linux
runs-on: ubuntu-22.04
steps:
@@ -180,7 +199,7 @@ jobs:
if: inputs.publish-snap
- name: Download build
uses: actions/download-artifact@v3
uses: actions/download-artifact@v4
with:
name: build
path: ./apps/web/build
@@ -220,17 +239,27 @@ jobs:
build-windows:
name: Build for Windows
needs: build
if: inputs.build-windows
runs-on: windows-latest
steps:
- name: Check out Git repository
uses: actions/checkout@v3
- name: Setup .NET Core SDK
uses: actions/setup-dotnet@v2
with:
dotnet-version: 6.0.x
- name: Install AzureCodeSigning
run: Install-Module -Name AzureCodeSigning -RequiredVersion 0.3.0 -Force -Repository PSGallery
shell: pwsh
- name: Setup Node
uses: ./.github/actions/setup-node-with-cache
- name: Download build
uses: actions/download-artifact@v3
uses: actions/download-artifact@v4
with:
name: build
path: ./apps/web/build
@@ -246,6 +275,9 @@ jobs:
- name: Publish
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }}
AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }}
AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }}
run: |
if ($${{ inputs.publish-github }} -eq $true) {
yarn electron-builder --win --publish always

104
.github/workflows/ios.publish.yml vendored Normal file
View File

@@ -0,0 +1,104 @@
name: Publish @notesnook/ios
on: workflow_dispatch
jobs:
build:
runs-on: macos-13
timeout-minutes: 60
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Setup Node
uses: ./.github/actions/setup-node-with-cache
- name: Install node modules
run: |
npm ci --ignore-scripts --prefer-offline --no-audit
npm run bootstrap -- --scope=mobile
- name: Build packages
run: npx nx run @notesnook/mobile:build
- name: ccache
uses: hendrikmuhs/ccache-action@v1.2.11
with:
key: ${{ runner.os }}-ccache-${{ hashFiles(format('apps/mobile/native/ios/Podfile.lock')) }}
max-size: 1500M
restore-keys: |
${{ runner.os }}-ccache-
- name: Setup ccache
run: |
ccache --set-config=base_dir=$PWD
ccache --set-config=inode_cache=true
ccache --set-config=compiler_check=content
ccache --set-config=depend_mode=true
ccache --set-config=file_clone=true
ccache --set-config=sloppiness=clang_index_store,file_stat_matches,include_file_ctime,include_file_mtime,ivfsoverlay,pch_defines,modules,system_headers,time_macros
ln -s $(which ccache) /usr/local/bin/gcc
ln -s $(which ccache) /usr/local/bin/g++
ln -s $(which ccache) /usr/local/bin/cc
ln -s $(which ccache) /usr/local/bin/c++
ln -s $(which ccache) /usr/local/bin/clang
ln -s $(which ccache) /usr/local/bin/clang++
ccache -p
- name: Cache Pods
uses: actions/cache@v2
id: pods-cache
with:
path: apps/mobile/native/ios/Pods
key: ${{ runner.os }}-pods-${{ hashFiles('apps/mobile/native/ios/Podfile.lock') }}
- name: Install Pods
run: npm run prepare:ios
- name: CCache Stats Before Build
run: ccache -sv
- name: Build iOS App
uses: ammarahm-ed/ios-build-action@master
with:
bundle-identifier: org.streetwriters.notesnook
scheme: Notesnook
configuration: "Release"
export-options: apps/mobile/native/ios/ExportOptions.plist
project-path: apps/mobile/native/ios/Notesnook.xcodeproj
workspace-path: apps/mobile/native/ios/Notesnook.xcworkspace
update-targets: |
Notesnook
Make Note
NotesWidgetExtension
disable-targets: Notesnook-tvOS,Notesnook-tvOSTests,NotesnookTests
code-signing-identity: Apple Distribution
team-id: ${{ secrets.APPLE_TEAM_ID }}
p12-base64: ${{ secrets.APPLE_CERTIFICATE_P12 }}
certificate-password: ${{ secrets.APPLE_CERTIFICATE_P12_PASSWORD }}
app-store-connect-api-key-issuer-id: ${{ secrets.API_KEY_ISSUER_ID }}
app-store-connect-api-key-id: ${{ secrets.APPSTORE_KEY_ID }}
app-store-connect-api-key-base64: ${{ secrets.APPSTORE_CONNECT_API_KEY_BASE64 }}
output-path: Notesnook.ipa
mobileprovision-base64: |
${{ secrets.APPLE_MOBILE_PROVISION_APP }}
${{ secrets.APPLE_MOBILE_PROVISION_SHARE }}
${{ secrets.APPLE_MOBILE_PROVISION_WIDGET }}
- name: CCache Stats After Build
run: ccache -sv
- name: "Upload app to TestFlight"
uses: apple-actions/upload-testflight-build@v1
with:
app-path: Notesnook.ipa
issuer-id: ${{ secrets.API_KEY_ISSUER_ID }}
api-key-id: ${{ secrets.API_KEY_ID }}
api-private-key: ${{ secrets.API_KEY }}
- name: Upload Notesnook.ipa to Github
uses: actions/upload-artifact@v2
with:
name: Notesnook.ipa
path: Notesnook.ipa

View File

@@ -0,0 +1,31 @@
name: Publish @notesnook/theme-builder
on: workflow_dispatch
jobs:
build:
name: Build
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Setup Node
uses: ./.github/actions/setup-node-with-cache
- name: Install packages
run: |
npm ci --ignore-scripts --prefer-offline --no-audit
npm run bootstrap -- --scope=themebuilder
- name: Setup environment
run: |
echo "NX_CLOUD_ACCESS_TOKEN=${{ secrets.NX_CLOUD_ACCESS_TOKEN }}" >> $GITHUB_ENV
echo "CLOUDFLARE_ACCOUNT_ID=${{ secrets.CLOUDFLARE_ACCOUNT_ID }}" >> $GITHUB_ENV
echo "CLOUDFLARE_API_TOKEN=${{ secrets.CLOUDFLARE_API_TOKEN }}" >> $GITHUB_ENV
- name: Generate build
run: npm run build:theme-builder
- name: Publish to Cloudflare Pages
run: npx --yes wrangler pages deploy --project-name=notesnook-theme-builder ./apps/theme-builder/build/

31
.github/workflows/vericrypt.publish.yml vendored Normal file
View File

@@ -0,0 +1,31 @@
name: Publish @notesnook/vericrypt
on: workflow_dispatch
jobs:
build:
name: Build
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Setup Node
uses: ./.github/actions/setup-node-with-cache
- name: Install packages
run: |
npm ci --ignore-scripts --prefer-offline --no-audit
npm run bootstrap -- --scope=vericrypt
- name: Setup environment
run: |
echo "NX_CLOUD_ACCESS_TOKEN=${{ secrets.NX_CLOUD_ACCESS_TOKEN }}" >> $GITHUB_ENV
echo "CLOUDFLARE_ACCOUNT_ID=${{ secrets.CLOUDFLARE_ACCOUNT_ID }}" >> $GITHUB_ENV
echo "CLOUDFLARE_API_TOKEN=${{ secrets.CLOUDFLARE_API_TOKEN }}" >> $GITHUB_ENV
- name: Generate build
run: npm run build:vericrypt
- name: Publish on Cloudflare Pages
run: npx --yes wrangler pages deploy --project-name=vericrypt ./apps/vericrypt/build/

69
.github/workflows/web.beta.publish.yml vendored Normal file
View File

@@ -0,0 +1,69 @@
name: Publish @notesnook/web Beta
on:
workflow_dispatch:
push:
branches:
- master
- "!v3-beta"
paths-ignore:
- "apps/mobile/**"
- "packages/editor-mobile/**"
- "apps/desktop/**"
- "apps/vericrypt/**"
- "servers/**"
- "packages/clipper/**"
- "extensions/web-clipper/**"
pull_request:
types:
- closed
- ready_for_review
- reopened
- opened
- synchronize
branches:
- master
- "!v3-beta"
paths-ignore:
- "apps/mobile/**"
- "packages/editor-mobile/**"
- "apps/desktop/**"
- "apps/vericrypt/**"
- "servers/**"
- "packages/clipper/**"
- "extensions/web-clipper/**"
jobs:
build:
name: Build
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Setup Node
uses: ./.github/actions/setup-node-with-cache
- name: Install packages
run: |
npm ci --ignore-scripts --prefer-offline --no-audit
npm run bootstrap -- --scope=web
- name: Setup environment
run: |
echo "NX_CLOUD_ACCESS_TOKEN=${{ secrets.NX_CLOUD_ACCESS_TOKEN }}" >> $GITHUB_ENV
echo "CLOUDFLARE_ACCOUNT_ID=${{ secrets.CLOUDFLARE_ACCOUNT_ID }}" >> $GITHUB_ENV
echo "CLOUDFLARE_API_TOKEN=${{ secrets.CLOUDFLARE_API_TOKEN }}" >> $GITHUB_ENV
- name: Generate beta build
run: npm run build:beta:web
- name: Publish to Cloudflare Pages
uses: unlike-ltd/github-actions-cloudflare-pages@v0.1.1
id: pages
with:
cloudflare-api-token: ${{ secrets.CLOUDFLARE_API_TOKEN }}
cloudflare-account-id: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
cloudflare-project-name: notesnook-beta-app
directory: ./apps/web/build
github-token: ${{ secrets.GITHUB_TOKEN }}
github-environment: ${{ (github.ref == 'refs/heads/master' && 'beta') || 'preview' }}

View File

@@ -24,8 +24,8 @@ jobs:
echo "CLOUDFLARE_ACCOUNT_ID=${{ secrets.CLOUDFLARE_ACCOUNT_ID }}" >> $GITHUB_ENV
echo "CLOUDFLARE_API_TOKEN=${{ secrets.CLOUDFLARE_API_TOKEN }}" >> $GITHUB_ENV
- name: Generate desktop build
- name: Generate build
run: npm run build:web
- name: Publish on Cloudflare Pages
run: npx --yes wrangler pages publish --project-name=notesnook-app ./apps/web/build/
- name: Publish to Cloudflare Pages
run: npx --yes wrangler pages deploy --project-name=notesnook-app ./apps/web/build/

View File

@@ -0,0 +1,32 @@
name: Publish @notesnook/web v3 Beta
on:
workflow_dispatch:
jobs:
build:
name: Build
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Setup Node
uses: ./.github/actions/setup-node-with-cache
- name: Install packages
run: |
npm ci --ignore-scripts --prefer-offline --no-audit
npm run bootstrap -- --scope=web
- name: Setup environment
run: |
echo "NX_CLOUD_ACCESS_TOKEN=${{ secrets.NX_CLOUD_ACCESS_TOKEN }}" >> $GITHUB_ENV
echo "CLOUDFLARE_ACCOUNT_ID=${{ secrets.CLOUDFLARE_ACCOUNT_ID }}" >> $GITHUB_ENV
echo "CLOUDFLARE_API_TOKEN=${{ secrets.CLOUDFLARE_API_TOKEN }}" >> $GITHUB_ENV
- name: Build
run: npm run build:web
- name: Publish to Cloudflare Pages
run: npx --yes wrangler pages deploy --project-name=notesnook-v3-beta ./apps/web/build/

3
.gitignore vendored
View File

@@ -9,4 +9,5 @@ nx-cloud.env
.env.local
.nyc_output
site
node_modules.backup
node_modules.backup
.nx/cache

View File

@@ -11,4 +11,5 @@ apps/mobile/native/
apps/web/public/an.js
# editor
packages/editor/styles/
packages/editor/styles/
/.nx/cache

View File

@@ -103,7 +103,7 @@ All commits must include valid scopes in the commit message. **Valid commit scop
**Packages:**
1. `crypto`: changes related to the cryptographic core (including `@notesnook/crypto-worker`)
1. `crypto`: changes related to the cryptographic core
2. `editor`: changes related to the editor (including `@notesnook/editor-mobile`)
3. `logger`: changes related to the logger
4. `theme`: changes related to the theme

View File

@@ -46,7 +46,6 @@ Notesnook is built using the following technologies:
| `@notesnook/editor-mobile` | [/packages/editor-mobile](/packages/editor-mobile) | A very thin wrapper around `@notesnook/editor` for mobile clients |
| `@notesnook/logger` | [/packages/logger](/packages/logger) | Simple & pluggable logger |
| `@notesnook/sodium` | [/packages/sodium](/packages/sodium) | Wrapper around libsodium to support Node.js & Browser |
| `@notesnook/crypto-worker` | [/packages/crypto-worker](/packages/crypto-worker) | Helpers to use `@notesnook/crypto` from a Worker |
| `@notesnook/streamable-fs` | [/packages/streamable-fs](/packages/streamable-fs) | Streaming interface around an IndexedDB based file system |
| `@notesnook/theme` | [/packages/theme](/packages/theme) | The core theme used in web & desktop clients |

View File

@@ -1,4 +1,5 @@
node_modules
build
output
dist
dist
_catalog

View File

@@ -0,0 +1,3 @@
owner: streetwriters
repo: notesnook
provider: github

View File

@@ -19,9 +19,40 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
/* eslint-disable no-var */
import { BrowserWindow } from "electron";
import {
type FormData as FormDataType,
type Headers as HeadersType,
type Request as RequestType,
type Response as ResponseType
} from "undici";
declare global {
var window: BrowserWindow | null;
var RELEASE: boolean;
var MAC_APP_STORE: boolean;
// Re-export undici fetch function and various classes to global scope.
// These are classes and functions expected to be at global scope according to Node.js v18 API
// documentation.
// See: https://nodejs.org/dist/latest-v18.x/docs/api/globals.html
// eslint-disable-next-line no-var
export var {
FormData,
Headers,
Request,
Response,
fetch
}: typeof import("undici");
type FormData = FormDataType;
type Headers = HeadersType;
type Request = RequestType;
type Response = ResponseType;
}
// NOTE: the import in the global block above needs to be a var for this to work properly.
globalThis.fetch = fetch;
globalThis.FormData = FormData;
globalThis.Headers = Headers;
globalThis.Request = Request;
globalThis.Response = Response;

File diff suppressed because it is too large Load Diff

View File

@@ -2,7 +2,7 @@
"name": "@notesnook/desktop",
"productName": "Notesnook",
"description": "Your private note taking space",
"version": "2.5.8",
"version": "3.0.3-beta",
"appAppleId": "1544027013",
"private": true,
"main": "./dist/index.js",
@@ -12,25 +12,26 @@
"license": "GPL-3.0-or-later",
"dependencies": {
"@notesnook/crypto": "file:../../packages/crypto",
"@trpc/client": "10.31.0",
"@trpc/server": "10.31.0",
"@trpc/client": "10.38.3",
"@trpc/server": "10.38.3",
"better-sqlite3-multiple-ciphers": "^9.4.0",
"electron-trpc": "0.5.2",
"electron-updater": "6.1.1",
"electron-updater": "6.1.4",
"icojs": "^0.17.1",
"sodium-native": "^4.0.6",
"typed-emitter": "^2.1.0",
"yargs": "^17.6.2",
"zod": "^3.21.4"
},
"devDependencies": {
"@types/node": "^18.15.0",
"@types/node": "18.16.1",
"@types/yargs": "^17.0.24",
"chokidar": "^3.5.3",
"electron": "24.5.1",
"electron-builder": "24.4.0",
"electron-builder-notarize": "^1.5.1",
"esbuild": "^0.17.19",
"node-fetch": "^3.3.1",
"tree-kill": "^1.2.2"
"electron": "^28.2.1",
"electron-builder": "^24.9.1",
"esbuild": "^0.20.0",
"tree-kill": "^1.2.2",
"undici": "^6.6.1"
},
"optionalDependencies": {
"dmg-license": "^1.0.11"
@@ -40,7 +41,7 @@
"staging": "node scripts/build.mjs --run",
"release": "node scripts/build.mjs",
"build": "tsc",
"bundle": "esbuild electron=./src/main.ts ./src/preload.ts --external:electron --external:fsevents --minify --bundle --outdir=./build --platform=node --tsconfig=tsconfig.json --define:MAC_APP_STORE=false --define:RELEASE=true",
"bundle": "esbuild electron=./src/main.ts ./src/preload.ts --external:electron --external:fsevents --external:better-sqlite3-multiple-ciphers --external:sodium-native --minify --bundle --outdir=./build --platform=node --tsconfig=tsconfig.json --define:MAC_APP_STORE=false --define:RELEASE=true",
"bundle:mas": "esbuild electron=./src/main.ts ./src/preload.ts --minify --external:electron --external:fsevents --bundle --outdir=./build --platform=node --tsconfig=tsconfig.json --define:MAC_APP_STORE=true --define:RELEASE=true",
"postinstall": "patch-package"
},
@@ -54,8 +55,8 @@
"productName": "Notesnook",
"copyright": "Copyright © 2023 Streetwriters (Private) Limited",
"artifactName": "notesnook_${os}_${arch}.${ext}",
"npmRebuild": false,
"asar": false,
"generateUpdatesFilesForAllChannels": true,
"files": [
"!*.chunk.js.map",
"!*.chunk.js.LICENSE.txt",
@@ -64,9 +65,17 @@
"!build/banner.jpg",
"!build/*.ico",
"!build/*.png",
"!node_modules${/*}"
"!node_modules${/*}",
"node_modules/better-sqlite3-multiple-ciphers/build/Release/better_sqlite3.node",
"node_modules/better-sqlite3-multiple-ciphers/lib",
"node_modules/better-sqlite3-multiple-ciphers/package.json",
"node_modules/file-uri-to-path",
"node_modules/bindings",
"node_modules/node-gyp-build",
"node_modules/sodium-native/prebuilds/${platform}-${arch}",
"node_modules/sodium-native/index.js",
"node_modules/sodium-native/package.json"
],
"afterSign": "electron-builder-notarize",
"afterPack": "./scripts/removeLocales.js",
"mac": {
"bundleVersion": "240",
@@ -94,7 +103,8 @@
"entitlements": "assets/entitlements.mac.plist",
"entitlementsInherit": "assets/entitlements.mac.plist",
"gatekeeperAssess": false,
"icon": "assets/icons/app.icns"
"icon": "assets/icons/app.icns",
"notarize": true
},
"dmg": {
"contents": [
@@ -135,6 +145,10 @@
]
}
],
"signingHashAlgorithms": [
"sha256"
],
"sign": "./scripts/sign.js",
"icon": "assets/icons/app.ico"
},
"portable": {
@@ -191,6 +205,7 @@
"allowNativeWayland": true
},
"extraResources": [
"app-update.yml",
"./assets/**"
],
"extraMetadata": {
@@ -204,7 +219,8 @@
{
"provider": "github",
"repo": "notesnook",
"owner": "streetwriters"
"owner": "streetwriters",
"channel": "beta"
}
]
}

View File

@@ -1,8 +1,8 @@
diff --git a/node_modules/app-builder-lib/out/targets/LinuxTargetHelper.js b/node_modules/app-builder-lib/out/targets/LinuxTargetHelper.js
index 3ba5d6a..cf8e147 100644
index fcb7f54..cb1c7f7 100644
--- a/node_modules/app-builder-lib/out/targets/LinuxTargetHelper.js
+++ b/node_modules/app-builder-lib/out/targets/LinuxTargetHelper.js
@@ -105,6 +105,7 @@ class LinuxTargetHelper {
@@ -116,6 +116,7 @@ class LinuxTargetHelper {
StartupWMClass: appInfo.productName,
...extra,
...targetSpecificOptions.desktop,
@@ -10,7 +10,7 @@ index 3ba5d6a..cf8e147 100644
};
const description = this.getDescription(targetSpecificOptions);
if (!(0, builder_util_1.isEmptyOrSpaces)(description)) {
@@ -148,6 +149,23 @@ class LinuxTargetHelper {
@@ -159,6 +160,23 @@ class LinuxTargetHelper {
data += `\n${name}=${desktopMeta[name]}`;
}
data += "\n";

View File

@@ -18,7 +18,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import path from "path";
import fs from "fs/promises";
import fs, { readFile, writeFile } from "fs/promises";
import { existsSync } from "fs";
import yargs from "yargs-parser";
import os from "os";
@@ -28,19 +28,7 @@ import { fileURLToPath } from "url";
const args = yargs(process.argv);
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const sodiumNativePrebuildPath = (arch) =>
path.join(
`node_modules`,
`@notesnook`,
`crypto`,
`node_modules`,
`@notesnook`,
`sodium`,
`node_modules`,
`sodium-native`,
`prebuilds`,
`${os.platform()}-${arch}`
);
const webAppPath = path.resolve(path.join(__dirname, "..", "..", "web"));
await fs.rm("./build/", { force: true, recursive: true });
@@ -49,6 +37,15 @@ if (args.rebuild || !existsSync(path.join(webAppPath, "build"))) {
await exec(`cd ${webAppPath} && npm run build:desktop`);
}
// temporary until there's support for prebuilt binaries for windows ARM & linux ARM
await patchBetterSQLite3();
if (os.platform() === "win32")
await exec(
"npx prebuildify --arch=arm64 --strip",
path.join(__dirname, "..", "node_modules", "sodium-native")
);
await fs.cp(path.join(webAppPath, "build"), "build", {
recursive: true,
force: true
@@ -62,42 +59,6 @@ if (args.variant === "mas") {
await exec(`npx tsc`);
if (existsSync(sodiumNativePrebuildPath("x64"))) {
console.log("copying sodium-native-x64");
await fs.cp(
sodiumNativePrebuildPath("x64"),
path.join("build", "prebuilds", `${process.platform}-x64`),
{
recursive: true,
force: true
}
);
}
if (existsSync(sodiumNativePrebuildPath("ia32"))) {
console.log("copying sodium-native-ia32");
await fs.cp(
sodiumNativePrebuildPath("ia32"),
path.join("build", "prebuilds", `${process.platform}-ia32`),
{
recursive: true,
force: true
}
);
}
if (existsSync(sodiumNativePrebuildPath("arm64"))) {
console.log("copying sodium-native-arm64");
await fs.cp(
sodiumNativePrebuildPath("arm64"),
path.join("build", "prebuilds", `${process.platform}-arm64`),
{
recursive: true,
force: true
}
);
}
if (args.run) {
await exec(`npx electron-builder --dir --x64`);
if (process.platform === "win32") {
@@ -109,9 +70,28 @@ if (args.run) {
}
}
async function exec(cmd) {
async function exec(cmd, cwd) {
return childProcess.execSync(cmd, {
env: process.env,
stdio: "inherit"
env: { ...process.env, NOTESNOOK_STAGING: true },
stdio: "inherit",
cwd: cwd || process.cwd()
});
}
async function patchBetterSQLite3() {
const jsonPath = path.join(
__dirname,
"..",
"node_modules",
"better-sqlite3-multiple-ciphers",
"package.json"
);
const json = JSON.parse(await readFile(jsonPath, "utf-8"));
json.version = "9.4.1";
json.homepage = "https://github.com/thecodrr/better-sqlite3-multiple-ciphers";
json.repository.url =
"git://github.com/thecodrr/better-sqlite3-multiple-ciphers.git";
await writeFile(jsonPath, JSON.stringify(json));
}

View File

@@ -27,21 +27,15 @@ import crypto from "crypto";
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const sodiumNativePrebuildPath = path.join(
`node_modules`,
`@notesnook`,
`crypto`,
`node_modules`,
`@notesnook`,
`sodium`,
`node_modules`,
`sodium-native`,
`prebuilds`
);
const RUNNING_PROCESSES = [];
const RESTARTABLE_PROCESSES = [];
let lastBundleHash = null;
const ENV = {
...process.env,
NO_COLOR: "true",
FORCE_COLOR: "false",
COLOR: "0"
};
await onChange(true);
console.log("Watching...");
@@ -58,6 +52,8 @@ process.on("SIGINT", async (s) => {
async function onChange(first) {
if (first) {
await fs.rm("./build/", { force: true, recursive: true });
await exec("npx electron-rebuild");
}
await exec(`npm run bundle`);
@@ -68,13 +64,6 @@ async function onChange(first) {
return;
}
if (first) {
await fs.cp(sodiumNativePrebuildPath, "build/prebuilds", {
recursive: true,
force: true
});
}
if (first) {
await spawnAndWaitUntil(
["npx", "nx", "start:desktop", "@notesnook/web"],
@@ -102,7 +91,7 @@ function spawnAndWaitUntil(cmd, cwd, predicate) {
const s = spawn(cmd[0], cmd.slice(1), {
cwd,
env: { ...process.env, NO_COLOR: "true" },
env: ENV,
shell: false
});
@@ -116,14 +105,15 @@ function spawnAndWaitUntil(cmd, cwd, predicate) {
});
}
async function exec(cmd) {
async function exec(cmd, cwd) {
try {
console.log(">", cmd);
return execSync(cmd, {
env: process.env,
env: ENV,
stdio: "inherit",
shell: false
shell: false,
cwd: cwd || process.cwd()
});
} catch {
//ignore
@@ -136,7 +126,7 @@ function execAsync(cmd, args, restartable, onExit) {
const proc = spawn(cmd, args, {
stdio: "inherit",
env: process.env,
env: ENV,
shell: false
});
@@ -179,9 +169,10 @@ async function cleanup() {
}
async function isBundleSame() {
const bundle = await fs.readFile(
path.join(__dirname, "..", "build", "electron.js")
);
const bundle = Buffer.concat([
await fs.readFile(path.join(__dirname, "..", "build", "electron.js")),
await fs.readFile(path.join(__dirname, "..", "build", "preload.js"))
]);
const hashSum = crypto.createHash("sha256");
hashSum.update(bundle);

View File

@@ -0,0 +1,59 @@
/*
This file is part of the Notesnook project (https://notesnook.com/)
Copyright (C) 2023 Streetwriters (Private) Limited
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
const { writeFileSync, rmSync } = require("fs");
const { execSync } = require("child_process");
const { relative, join } = require("path");
module.exports = async function (configuration) {
if (process.env.NOTESNOOK_STAGING) return;
const Endpoint = "https://weu.codesigning.azure.net";
const CodeSigningAccountName = "Notesnook";
const CertificateProfileName = "Notesnook";
const FileDigest = configuration.hash.toUpperCase();
const TimestampRfc3161 = "http://timestamp.acs.microsoft.com";
const TimestampDigest = configuration.hash.toUpperCase();
const Description = "The Notesnook app";
const DescriptionUrl = "https://notesnook.com/";
const FilesCatalog = createCatalog(configuration.path);
const command = `Invoke-AzureCodeSigning -Endpoint "${Endpoint}" -CodeSigningAccountName "${CodeSigningAccountName}" -CertificateProfileName "${CertificateProfileName}" -FileDigest "${FileDigest}" -TimestampRfc3161 "${TimestampRfc3161}" -TimestampDigest "${TimestampDigest}" -Description "${Description}" -DescriptionUrl "${DescriptionUrl}" -FilesCatalog "${FilesCatalog}"`;
console.debug("Signing", configuration.path, "using command", command);
psexec(command);
console.debug("Signed", configuration.path);
rmSync(FilesCatalog);
};
function createCatalog(path) {
const catalogPath = join(__dirname, "_catalog");
writeFileSync(catalogPath, relative(__dirname, path));
return catalogPath;
}
function psexec(cmd) {
return execSync(cmd, {
env: process.env,
stdio: "inherit",
shell: "pwsh"
});
}

View File

@@ -23,6 +23,7 @@ import { osIntegrationRouter } from "./os-integration";
import { spellCheckerRouter } from "./spell-checker";
import { updaterRouter } from "./updater";
import { bridgeRouter } from "./bridge";
import { safeStorageRouter } from "./safe-storage";
const t = initTRPC.create();
@@ -31,7 +32,8 @@ export const router = t.router({
integration: osIntegrationRouter,
spellChecker: spellCheckerRouter,
updater: updaterRouter,
bridge: bridgeRouter
bridge: bridgeRouter,
safeStorage: safeStorageRouter
});
export const api = router.createCaller({});

View File

@@ -29,6 +29,9 @@ import { dirname } from "path";
import { resolvePath } from "../utils/resolve-path";
import { observable } from "@trpc/server/observable";
import { AssetManager } from "../utils/asset-manager";
import { isFlatpak } from "../utils";
import { setupDesktopIntegration } from "../utils/desktop-integration";
import { rm } from "fs/promises";
const t = initTRPC.create();
@@ -44,6 +47,8 @@ const NotificationOptions = z.object({
});
export const osIntegrationRouter = t.router({
isFlatpak: t.procedure.query(() => isFlatpak()),
zoomFactor: t.procedure.query(() => config.zoomFactor),
setZoomFactor: t.procedure.input(z.number()).mutation(({ input: factor }) => {
globalThis.window?.webContents.setZoomFactor(factor);
@@ -52,21 +57,21 @@ export const osIntegrationRouter = t.router({
privacyMode: t.procedure.query(() => config.privacyMode),
setPrivacyMode: t.procedure
.input(z.boolean().optional())
.mutation(({ input: privacyMode }) => {
.input(z.object({ enabled: z.boolean() }))
.mutation(({ input: { enabled } }) => {
if (!globalThis.window || !["win32", "darwin"].includes(process.platform))
return;
globalThis.window.setContentProtection(!!privacyMode);
globalThis.window.setContentProtection(enabled);
if (process.platform === "win32") {
globalThis.window.setThumbnailClip(
privacyMode
enabled
? { x: 0, y: 0, width: 1, height: 1 }
: { x: 0, y: 0, width: 0, height: 0 }
);
}
config.privacyMode = !!privacyMode;
config.privacyMode = enabled;
}),
desktopIntegration: t.procedure.query(() => config.desktopSettings),
@@ -79,6 +84,7 @@ export const osIntegrationRouter = t.router({
AutoLaunch.disable();
}
config.desktopSettings = settings;
setupDesktopIntegration(settings);
}),
selectDirectory: t.procedure
@@ -117,6 +123,17 @@ export const osIntegrationRouter = t.router({
writeFileSync(resolvedPath, data);
}),
resolvePath: t.procedure
.input(z.object({ filePath: z.string() }))
.query(({ input }) => {
const { filePath } = input;
return resolvePath(filePath);
}),
deleteFile: t.procedure.input(z.string()).query(async ({ input }) => {
await rm(input);
}),
showNotification: t.procedure
.input(NotificationOptions)
.query(({ input }) => {

View File

@@ -16,24 +16,23 @@ 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 { safeStorage } from "electron";
import { initTRPC } from "@trpc/server";
import { z } from "zod";
import { groupArray } from "../utils/grouping";
const t = initTRPC.create();
test("group alphabetically", () => {
const sortedAlphabet = "abcdefghijlmnopqrstuvwxyz"
.split("")
.map((a) => ({ title: a }));
const alphabet = "nopqrstuvwxyzabcdefghijlm"
.split("")
.map((a) => ({ title: a, item: true }));
let ret = groupArray(alphabet, {
groupBy: "abc",
sortDirection: "asc",
sortBy: "title"
}).filter((v) => !v.item);
expect(
sortedAlphabet.every((alpha, index) => {
return ret[index].title === alpha.title.toUpperCase();
})
).toBeTruthy();
export const safeStorageRouter = t.router({
/**
* Takes a string and returns an encrypted base64 string
*/
encryptString: t.procedure.input(z.string()).query(async ({ input }) => {
return safeStorage.encryptString(input).toString("base64");
}),
/**
* Takes an encrypted base64 string and returns a string
*/
decryptString: t.procedure.input(z.string()).query(async ({ input }) => {
return safeStorage.decryptString(Buffer.from(input, "base64"));
})
});

View File

@@ -114,9 +114,9 @@ export const spellCheckerRouter = t.router({
globalThis.window?.webContents.session.setSpellCheckerLanguages(languages)
),
toggle: t.procedure
.input(z.boolean().optional())
.mutation(({ input: enabled }) => {
globalThis.window?.webContents.session.setSpellCheckerEnabled(!!enabled);
config.isSpellCheckerEnabled = !!enabled;
.input(z.object({ enabled: z.boolean() }))
.mutation(({ input: { enabled } }) => {
globalThis.window?.webContents.session.setSpellCheckerEnabled(enabled);
config.isSpellCheckerEnabled = enabled;
})
});

View File

@@ -41,10 +41,9 @@ export const updaterRouter = t.router({
}),
toggleAutoUpdates: t.procedure
.input(z.boolean().optional())
.mutation(({ input }) => {
config.automaticUpdates =
input === undefined ? !config.automaticUpdates : input;
.input(z.object({ enabled: z.boolean() }))
.mutation(({ input: { enabled } }) => {
config.automaticUpdates = enabled;
}),
onChecking: createSubscription("checking-for-update"),

View File

@@ -27,19 +27,23 @@ export type CLIOptions = {
hidden: boolean;
};
export async function parseArguments(): Promise<CLIOptions> {
export async function parseArguments(argv: string[]): Promise<CLIOptions> {
const result: CLIOptions = {
note: false,
notebook: false,
reminder: false,
hidden: false
};
const { hidden } = await yargs(hideBin(process.argv))
const { hidden } = await yargs(hideBin(argv))
.boolean("hidden")
// have to account for this flag added on Windows when launching
// via Jumplist
.boolean("allow-file-access-from-files")
.command("new", "Create a new item", (yargs) => {
return yargs
.command("note", "Create a new note", {}, () => {
result.note = true;
console.log("HERE!");
})
.command("notebook", "Create a new notebook", {}, () => {
result.notebook = true;

View File

@@ -20,3 +20,4 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
export * from "./constants";
export type { AppRouter } from "./api";
export { type UpdateInfo } from "builder-util-runtime";
export { type DesktopIntegration } from "./utils/config";

View File

@@ -17,14 +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 { app, BrowserWindow, nativeTheme, session, shell } from "electron";
import { app, BrowserWindow, nativeTheme, shell } from "electron";
import { isDevelopment } from "./utils";
import { registerProtocol, PROTOCOL_URL } from "./utils/protocol";
import { configureAutoUpdater } from "./utils/autoupdater";
import { getBackgroundColor, getTheme, setTheme } from "./utils/theme";
import { setupMenu } from "./utils/menu";
import { WindowState } from "./utils/window-state";
import { AutoLaunch } from "./utils/autolaunch";
import { setupJumplist } from "./utils/jumplist";
import { setupTray } from "./utils/tray";
import { CLIOptions, parseArguments } from "./cli";
@@ -33,6 +32,15 @@ import { createIPCHandler } from "electron-trpc/main";
import { router, api } from "./api";
import { config } from "./utils/config";
import path from "path";
import { bringToFront } from "./utils/bring-to-front";
import { bridge } from "./api/bridge";
import { setupDesktopIntegration } from "./utils/desktop-integration";
// only run a single instance
if (!MAC_APP_STORE && !app.requestSingleInstanceLock()) {
console.log("Another instance is already running!");
app.exit();
}
if (process.platform == "win32" && process.env.PORTABLE_EXECUTABLE_DIR) {
console.log("Portable app: true");
@@ -42,19 +50,21 @@ if (process.platform == "win32" && process.env.PORTABLE_EXECUTABLE_DIR) {
app.setPath("userData", path.join(root, "UserData"));
}
// only run a single instance
if (!MAC_APP_STORE && !app.requestSingleInstanceLock()) {
app.exit();
}
if (process.platform === "win32") {
app.setAppUserModelId(app.name);
}
process.on("uncaughtException", (error) => {
console.error("uncaughtException:", error);
});
process.on("unhandledRejection", (reason) => {
console.error("unhandledRejection:", reason);
});
app.commandLine.appendSwitch("lang", "en-US");
async function createWindow() {
const cliOptions = await parseArguments();
const cliOptions = await parseArguments(process.argv);
setTheme(getTheme());
const mainWindowState = new WindowState({});
@@ -67,7 +77,7 @@ async function createWindow() {
darkTheme: getTheme() === "dark",
backgroundColor: getBackgroundColor(),
opacity: 0,
autoHideMenuBar: true,
autoHideMenuBar: false,
icon: AssetManager.appIcon({
size: 512,
format: process.platform === "win32" ? "ico" : "png"
@@ -81,7 +91,9 @@ async function createWindow() {
}
});
createIPCHandler({ router, windows: [mainWindow] });
globalThis.window = mainWindow;
mainWindow.setMenuBarVisibility(false);
mainWindowState.manage(mainWindow);
if (cliOptions.hidden && !config.desktopSettings.minimizeToSystemTray)
@@ -91,12 +103,11 @@ async function createWindow() {
mainWindow.setOpacity(1);
if (config.privacyMode) {
await api.integration.setPrivacyMode(config.privacyMode);
await api.integration.setPrivacyMode({ enabled: config.privacyMode });
}
await AssetManager.loadIcons();
setupDesktopIntegration();
createIPCHandler({ router, windows: [mainWindow] });
setupDesktopIntegration(config.desktopSettings);
mainWindow.webContents.session.setSpellCheckerDictionaryDownloadURL(
"http://dictionaries.notesnook.com/"
@@ -137,6 +148,15 @@ app.once("window-all-closed", () => {
}
});
app.on("second-instance", async (_ev, argv) => {
if (!globalThis.window) return;
const cliOptions = await parseArguments(argv);
if (cliOptions.note) bridge.onCreateItem("note");
if (cliOptions.notebook) bridge.onCreateItem("notebook");
if (cliOptions.reminder) bridge.onCreateItem("reminder");
bringToFront();
});
app.on("activate", () => {
if (globalThis.window === null) {
createWindow();
@@ -157,49 +177,3 @@ function createURL(options: CLIOptions, path = "/") {
return url;
}
function setupDesktopIntegration() {
const desktopIntegration = config.desktopSettings;
if (
desktopIntegration.closeToSystemTray ||
desktopIntegration.minimizeToSystemTray
) {
setupTray();
}
// when close to system tray is enabled, it becomes nigh impossible
// to "quit" the app. This is necessary in order to fix that.
if (desktopIntegration.closeToSystemTray) {
app.on("before-quit", () => app.exit(0));
}
globalThis.window?.once("close", (e) => {
if (config.desktopSettings.closeToSystemTray) {
e.preventDefault();
if (process.platform == "darwin") {
// on macOS window cannot be minimized/hidden if it is already fullscreen
// so we just close it.
if (globalThis.window?.isFullScreen()) app.exit(0);
else app.hide();
} else {
globalThis.window?.minimize();
globalThis.window?.hide();
}
}
});
globalThis.window?.on("minimize", () => {
if (config.desktopSettings.minimizeToSystemTray) {
if (process.platform == "darwin") {
app.hide();
} else {
globalThis.window?.hide();
}
}
});
if (desktopIntegration.autoStart) {
AutoLaunch.enable(!!desktopIntegration.startMinimized);
}
}

View File

@@ -23,15 +23,16 @@ import { type RendererGlobalElectronTRPC } from "electron-trpc/src/types";
import type { NNCrypto } from "@notesnook/crypto";
import { ipcRenderer } from "electron";
import { platform } from "os";
import sqlite3, { Database } from "better-sqlite3-multiple-ciphers";
declare global {
var os: () => "mas" | ReturnType<typeof platform>;
var electronTRPC: RendererGlobalElectronTRPC;
var NativeNNCrypto: (new () => NNCrypto) | undefined;
var createSQLite3Database: (filename: string) => Database;
}
process.once("loaded", async () => {
console.log("HELLO!");
const electronTRPC: RendererGlobalElectronTRPC = {
sendMessage: (operation) =>
ipcRenderer.send(ELECTRON_TRPC_CHANNEL, operation),
@@ -41,11 +42,6 @@ process.once("loaded", async () => {
globalThis.electronTRPC = electronTRPC;
});
globalThis.NativeNNCrypto =
process.platform === "win32" &&
process.arch !== "x64" &&
process.arch !== "ia32"
? undefined
: require("@notesnook/crypto").NNCrypto;
globalThis.NativeNNCrypto = require("@notesnook/crypto").NNCrypto;
globalThis.createSQLite3Database = (filename) => sqlite3(filename);
globalThis.os = () => (MAC_APP_STORE ? "mas" : platform());

View File

@@ -35,6 +35,8 @@ const LINUX_AUTOSTART_DIRECTORY_PATH = path.join(
"autostart"
);
const STARTUP_ARGS = ["--hidden"];
export class AutoLaunch {
static enable(hidden: boolean) {
if (process.platform === "linux") {
@@ -47,10 +49,14 @@ export class AutoLaunch {
LINUX_DESKTOP_ENTRY(hidden)
);
} else {
const loginItemSettings = app.getLoginItemSettings({
args: STARTUP_ARGS
});
if (loginItemSettings.openAtLogin) return;
app.setLoginItemSettings({
openAtLogin: true,
openAsHidden: hidden,
args: ["--hidden"]
args: STARTUP_ARGS
});
}
}

View File

@@ -20,11 +20,15 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
import { autoUpdater } from "electron-updater";
import { config } from "./config";
const CHANNEL = autoUpdater.currentVersion.raw.endsWith("-beta")
? "beta"
: "latest";
async function configureAutoUpdater() {
autoUpdater.setFeedURL({
provider: "generic",
url: `https://notesnook.com/releases/${process.platform}/`,
useMultipleRangeRequest: false
url: `https://notesnook.com/api/v1/releases/${process.platform}/${CHANNEL}`,
useMultipleRangeRequest: false,
channel: CHANNEL
});
autoUpdater.autoDownload = config.automaticUpdates;

View File

@@ -38,8 +38,8 @@ export const config = {
closeToSystemTray: false
},
privacyMode: false,
isSpellCheckerEnabled: false,
zoomFactor: 0,
isSpellCheckerEnabled: true,
zoomFactor: 1,
theme: nativeTheme.themeSource,
automaticUpdates: true
};

View File

@@ -0,0 +1,74 @@
/*
This file is part of the Notesnook project (https://notesnook.com/)
Copyright (C) 2023 Streetwriters (Private) Limited
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { app } from "electron";
import { DesktopIntegration, config } from "./config";
import { setupTray, destroyTray } from "./tray";
import { AutoLaunch } from "./autolaunch";
export function setupDesktopIntegration(
desktopIntegration: DesktopIntegration
) {
if (
desktopIntegration.closeToSystemTray ||
desktopIntegration.minimizeToSystemTray
) {
setupTray();
} else {
destroyTray();
}
// when close to system tray is enabled, it becomes nigh impossible
// to "quit" the app. This is necessary in order to fix that.
app.on("before-quit", () =>
desktopIntegration.closeToSystemTray ? app.exit(0) : null
);
globalThis.window?.on("close", (e) => {
if (config.desktopSettings.closeToSystemTray) {
e.preventDefault();
if (process.platform == "darwin") {
// on macOS window cannot be minimized/hidden if it is already fullscreen
// so we just close it.
if (globalThis.window?.isFullScreen()) app.exit(0);
else app.hide();
} else {
try {
globalThis.window?.minimize();
globalThis.window?.hide();
} catch (error) {
console.error(error);
}
}
}
});
globalThis.window?.on("minimize", () => {
if (config.desktopSettings.minimizeToSystemTray) {
if (process.platform == "darwin") {
app.hide();
} else {
globalThis.window?.hide();
}
}
});
if (desktopIntegration.autoStart) {
AutoLaunch.enable(!!desktopIntegration.startMinimized);
}
}

View File

@@ -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 { protocol, ProtocolRequest } from "electron";
import { protocol } from "electron";
import { isDevelopment } from "./index";
import { createReadStream } from "fs";
import { extname, normalize } from "path";
import { URL } from "url";
import fetch, { Response } from "node-fetch";
const BASE_PATH = isDevelopment() ? "../public" : "";
const HOSTNAME = `app.notesnook.com`;
const FILE_NOT_FOUND = -6;
const SCHEME = "https";
const extensionToMimeType: Record<string, string> = {
html: "text/html",
@@ -34,101 +32,59 @@ const extensionToMimeType: Record<string, string> = {
js: "application/javascript",
css: "text/css",
svg: "image/svg+xml",
png: "image/png"
png: "image/png",
jpg: "image/jpg",
ttf: "font/ttf",
woff: "font/woff",
woff2: "font/woff2"
};
function registerProtocol() {
const protocolInterceptionResult = protocol.interceptStreamProtocol(
SCHEME,
async (request, callback) => {
const url = new URL(request.url);
if (shouldInterceptRequest(url)) {
console.info("Intercepting request:", request.url);
const loadIndex = !extname(url.pathname);
const filePath = normalize(
`${__dirname}${
loadIndex
? `${BASE_PATH}/index.html`
: `${BASE_PATH}/${url.pathname}`
}`
);
if (!filePath) {
console.error("Local asset file not found at", filePath);
callback({ error: FILE_NOT_FOUND });
return;
}
const fileExtension = extname(filePath).replace(".", "");
const data = createReadStream(filePath);
callback({
data,
mimeType: extensionToMimeType[fileExtension]
});
} else {
let response: Response;
try {
const body = await getBody(request);
response = await fetch(request.url, {
...request,
body,
headers: {
...request.headers
// origin: `${PROTOCOL}://${HOSTNAME}/`
},
referrer: request.referrer,
redirect: "manual"
});
} catch (e) {
console.error(e);
console.error(`Error sending request to `, request.url, "Error: ", e);
callback({ statusCode: 400 });
return;
}
callback({
statusCode: response.status,
data: response.body || undefined,
headers: Object.fromEntries(response.headers.entries()),
mimeType: response.headers.get("Content-Type") || undefined
protocol.handle(SCHEME, async (request) => {
const url = new URL(request.url);
if (shouldInterceptRequest(url)) {
console.info("Intercepting request:", request.url);
const loadIndex = !extname(url.pathname);
const filePath = normalize(
`${__dirname}${
loadIndex ? `${BASE_PATH}/index.html` : `${BASE_PATH}/${url.pathname}`
}`
);
if (!filePath) {
console.error("Local asset file not found at", filePath);
return new Response(undefined, {
status: 404,
statusText: "FILE_NOT_FOUND"
});
}
const fileExtension = extname(filePath).replace(".", "");
return new Response(createReadStream(filePath), {
headers: { "Content-Type": extensionToMimeType[fileExtension] }
});
} else {
if (request.headers.has("X-Content-Length")) {
request.headers.set(
"Content-Length",
request.headers.get("X-Content-Length") || "0"
);
request.headers.delete("X-Content-Length");
}
const headers = Object.fromEntries(request.headers.entries());
return await fetch(request.url, {
signal: request.signal,
mode: request.mode,
headers,
method: request.method,
body: request.body,
credentials: request.credentials,
referrer: (request as any).referrer,
duplex: "half",
redirect: "manual"
});
}
);
console.info(
`${SCHEME} protocol inteception ${
protocolInterceptionResult ? "successful" : "failed"
}.`
);
// protocol.handle(SCHEME, (request) => {
// const url = new URL(request.url);
// if (shouldInterceptRequest(url)) {
// console.info("Intercepting request:", request.url);
// const loadIndex = !extname(url.pathname);
// const absoluteFilePath = normalize(
// `${__dirname}${
// loadIndex ? `${BASE_PATH}/index.html` : `${BASE_PATH}/${url.pathname}`
// }`
// );
// const filePath = getPath(absoluteFilePath);
// if (!filePath) {
// console.error("Local asset file not found at", filePath);
// return new Response(undefined, {
// status: 404,
// statusText: "FILE_NOT_FOUND"
// });
// }
// const fileExtension = extname(filePath).replace(".", "");
// const data = createReadStream(filePath);
// return new Response(data, {
// headers: { "Content-Type": extensionToMimeType[fileExtension] }
// });
// } else {
// return net.fetch(request);
// }
// });
// console.info(`${SCHEME} protocol inteception "successful"`);
});
console.info(`${SCHEME} protocol inteception "successful"`);
}
const bypassedRoutes: string[] = [];
@@ -139,22 +95,3 @@ function shouldInterceptRequest(url: URL) {
const PROTOCOL_URL = `${SCHEME}://${HOSTNAME}/`;
export { registerProtocol, PROTOCOL_URL };
async function getBody(request: ProtocolRequest) {
const session = globalThis?.window?.webContents?.session;
const blobParts = [];
if (!request.uploadData || !request.uploadData.length) return null;
for (const data of request.uploadData) {
if (data.bytes) {
blobParts.push(new Uint8Array(data.bytes));
} else if (session && data.blobUUID) {
const buffer = await session.getBlobData(data.blobUUID);
if (!buffer) continue;
blobParts.push(new Uint8Array(buffer));
}
}
const blob = new Blob(blobParts);
return blob;
}

View File

@@ -5,7 +5,6 @@ artifacts/
.DS_Store
native/android/app/src/main/assets/
*Issues.md
build_cache/
#

View File

@@ -1,90 +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 React, { useEffect } from "react";
import { View } from "react-native";
import "react-native-gesture-handler";
import { GestureHandlerRootView } from "react-native-gesture-handler";
import { SafeAreaProvider } from "react-native-safe-area-context";
import AppLockedOverlay from "./components/app-lock-overlay";
import { withErrorBoundry } from "./components/exception-handler";
import GlobalSafeAreaProvider from "./components/globalsafearea";
import { useAppEvents } from "./hooks/use-app-events";
import { ApplicationHolder } from "./navigation";
import Notifications from "./services/notifications";
import SettingsService from "./services/settings";
import { TipManager } from "./services/tip-manager";
import { useUserStore } from "./stores/use-user-store";
SettingsService.init();
SettingsService.checkOrientation();
const App = () => {
const init = useAppEvents();
useEffect(() => {
let { appLockMode } = SettingsService.get();
if (appLockMode && appLockMode !== "none") {
useUserStore.getState().lockApp(true);
}
init();
setTimeout(async () => {
SettingsService.onFirstLaunch();
await Notifications.get();
if (SettingsService.get().notifNotes) {
Notifications.pinQuickNote(true);
}
TipManager.init();
}, 100);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
return (
<View
style={{
height: "100%",
width: "100%"
}}
>
<View
style={{
position: "absolute",
width: "100%",
height: "100%",
zIndex: -1
}}
pointerEvents="none"
>
<SafeAreaProvider>
<GlobalSafeAreaProvider />
</SafeAreaProvider>
</View>
<GestureHandlerRootView
style={{
height: "100%",
width: "100%"
}}
>
<ApplicationHolder />
</GestureHandlerRootView>
<AppLockedOverlay />
</View>
);
};
export default withErrorBoundry(App, "App");

164
apps/mobile/app/app.tsx Normal file
View File

@@ -0,0 +1,164 @@
/*
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 "@azure/core-asynciterator-polyfill";
import "react-native-gesture-handler";
import {
THEME_COMPATIBILITY_VERSION,
useThemeEngineStore
} from "@notesnook/theme";
import React, { useEffect } from "react";
import { I18nManager, View } from "react-native";
import "react-native-gesture-handler";
import { GestureHandlerRootView } from "react-native-gesture-handler";
import { SafeAreaProvider } from "react-native-safe-area-context";
import AppLockedOverlay from "./components/app-lock-overlay";
import { withErrorBoundry } from "./components/exception-handler";
import GlobalSafeAreaProvider from "./components/globalsafearea";
import { useAppEvents } from "./hooks/use-app-events";
import { ApplicationHolder } from "./navigation";
import { themeTrpcClient } from "./screens/settings/theme-selector";
import Notifications from "./services/notifications";
import SettingsService from "./services/settings";
import { TipManager } from "./services/tip-manager";
import { useThemeStore } from "./stores/use-theme-store";
import { useUserStore } from "./stores/use-user-store";
I18nManager.allowRTL(false);
I18nManager.forceRTL(false);
I18nManager.swapLeftAndRightInRTL(false);
// How app lock works
// 1. User goes to settings and setup app lock with a Pin/Password.
// 2. The Pin/Password is used to encrypt a random value or user's encryption key.
// 3. The encrypted value is stored in MMKV
// 4. When the app launches, the same value is decrypted with user provided key, if it works, we launch the app otherwise it remains locked.
// 5. If Biometrics are enabled, the app lock pin/password is stored in keychain. the value can be accessed if fingerprint auth works ONLY.
// 6. User can manually enter the pin if biometrics fails.
// 7. There is no way to enter the app if user forgets the PIN. The only way is to reset app data and start fresh again.
// How to handle app lock for existing users...
// 1.
const App = () => {
const init = useAppEvents();
useEffect(() => {
const { appLockEnabled, appLockMode } = SettingsService.get();
if (appLockEnabled || appLockMode !== "none") {
useUserStore.getState().lockApp(true);
}
//@ts-ignore
globalThis["IS_MAIN_APP_RUNNING"] = true;
init();
setTimeout(async () => {
SettingsService.onFirstLaunch();
await Notifications.get();
if (SettingsService.get().notifNotes) {
Notifications.pinQuickNote(true);
}
TipManager.init();
}, 100);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
return (
<View
style={{
height: "100%",
width: "100%"
}}
>
<View
style={{
position: "absolute",
width: "100%",
height: "100%",
zIndex: -1
}}
pointerEvents="none"
>
<SafeAreaProvider>
<GlobalSafeAreaProvider />
</SafeAreaProvider>
</View>
<GestureHandlerRootView
style={{
height: "100%",
width: "100%"
}}
>
<ApplicationHolder />
</GestureHandlerRootView>
<AppLockedOverlay />
</View>
);
};
let currTheme =
useThemeStore.getState().colorScheme === "dark"
? SettingsService.getProperty("darkTheme")
: SettingsService.getProperty("lighTheme");
useThemeEngineStore.getState().setTheme(currTheme);
export const withTheme = (Element: () => JSX.Element) => {
return function AppWithThemeProvider() {
const [colorScheme, darkTheme, lightTheme] = useThemeStore((state) => [
state.colorScheme,
state.darkTheme,
state.lightTheme
]);
useEffect(() => {
setTimeout(() => {
const currentTheme = colorScheme === "dark" ? darkTheme : lightTheme;
if (!currentTheme) return;
themeTrpcClient.updateTheme
.query({
version: currentTheme.version,
compatibilityVersion: THEME_COMPATIBILITY_VERSION,
id: currentTheme.id
})
.then((theme) => {
if (theme) {
console.log(theme.version, "theme updated");
theme.colorScheme === "dark"
? useThemeStore.getState().setDarkTheme(theme)
: useThemeStore.getState().setLightTheme(theme);
}
})
.catch(console.log);
}, 1000);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
const nextTheme = colorScheme === "dark" ? darkTheme : lightTheme;
if (JSON.stringify(nextTheme) !== JSON.stringify(currTheme)) {
useThemeEngineStore
.getState()
.setTheme(colorScheme === "dark" ? darkTheme : lightTheme);
currTheme = nextTheme;
}
return <Element />;
};
};
export default withTheme(withErrorBoundry(App, "App"));

View File

@@ -17,16 +17,39 @@ 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 Sodium from "@ammarahmed/react-native-sodium";
import { Platform } from "react-native";
import "react-native-get-random-values";
import * as Keychain from "react-native-keychain";
import { MMKVLoader, ProcessingModes } from "react-native-mmkv-storage";
import { generateSecureRandom } from "react-native-securerandom";
import Sodium from "@ammarahmed/react-native-sodium";
import { DatabaseLogger } from ".";
import { MMKV } from "./mmkv";
// Database key cipher is persisted across different user sessions hence it has
// it's independent storage which we will never clear. This is only used when application has
// app lock with password enabled.
export const CipherStorage = new MMKVLoader()
.withInstanceID("cipher_storage")
.setProcessingMode(
Platform.OS === "ios"
? ProcessingModes.MULTI_PROCESS
: ProcessingModes.SINGLE_PROCESS
)
.disableIndexing()
.initialize();
const IOS_KEYCHAIN_ACCESS_GROUP = "group.org.streetwriters.notesnook";
const IOS_KEYCHAIN_SERVICE_NAME = "org.streetwriters.notesnook";
const IOS_KEYCHAIN_UPGRAGE_KEY = "keychain-ios:upgraded";
const KEYCHAIN_SERVER_DBKEY = "notesnook:db";
const NOTESNOOK_APPLOCK_KEY_SALT = "3dqclWbOYllfk9kk";
const NOTESNOOK_DB_KEY_SALT = "2rcgSprDmRvZ1AAa";
const NOTESNOOK_USER_KEY_SALT = "7qO4qeoM6PbsAJ0Q";
const DB_KEY_CIPHER = "databaseKeyCipher";
const USER_KEY_CIPHER = "userKeyCipher";
const APPLOCK_CIPHER = "applockCipher";
const KEYSTORE_CONFIG = Platform.select({
ios: {
@@ -37,61 +60,232 @@ const KEYSTORE_CONFIG = Platform.select({
android: {}
});
export async function deriveCryptoKey(name, data) {
try {
let credentials = await Sodium.deriveKey(data.password, data.salt);
await Keychain.setInternetCredentials(
"notesnook",
name,
credentials.key,
KEYSTORE_CONFIG
function generatePassword() {
const length = 80;
const crypto = window.crypto || window.msCrypto;
if (typeof crypto === "undefined") {
throw new Error(
"Crypto API is not supported. Please upgrade your web browser"
);
MMKV.setBool(IOS_KEYCHAIN_UPGRAGE_KEY, true);
return credentials.key;
}
const charset =
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz!@#$%^&+_{}[]():<>/?;";
const indexes = crypto.getRandomValues(new Uint32Array(length));
let secret = "";
for (const index of indexes) {
secret += charset[index % charset.length];
}
return secret;
}
export async function encryptDatabaseKeyWithPassword(appLockPassword) {
const key = getDatabaseKey();
const appLockCredentials = await Sodium.deriveKey(
appLockPassword,
NOTESNOOK_APPLOCK_KEY_SALT
);
const databaseKeyCipher = await encrypt(appLockCredentials, key);
MMKV.setMap(DB_KEY_CIPHER, databaseKeyCipher);
// We reset the database key from keychain once app lock password is set.
await Keychain.resetInternetCredentials(KEYCHAIN_SERVER_DBKEY);
return true;
}
export async function restoreDatabaseKeyToKeyChain(appLockPassword) {
const databaseKeyCipher = CipherStorage.getMap(DB_KEY_CIPHER);
const databaseKey = await decrypt(
{
password: appLockPassword
},
databaseKeyCipher
);
await Keychain.setInternetCredentials(
KEYCHAIN_SERVER_DBKEY,
"notesnook",
databaseKey,
KEYSTORE_CONFIG
);
MMKV.removeItem(DB_KEY_CIPHER);
return true;
}
export async function setAppLockVerificationCipher(appLockPassword) {
try {
const appLockCredentials = await Sodium.deriveKey(
appLockPassword,
NOTESNOOK_APPLOCK_KEY_SALT
);
const encrypted = await encrypt(appLockCredentials, generatePassword());
CipherStorage.setMap(APPLOCK_CIPHER, encrypted);
DatabaseLogger.info("setAppLockVerificationCipher");
} catch (e) {
console.error(e);
DatabaseLogger.error(e);
console.log(e);
}
}
async function upgradeIOSKeychain(username, password) {
if (Platform.OS !== "ios") return;
if (!MMKV.getBool(IOS_KEYCHAIN_UPGRAGE_KEY)) {
await Keychain.setInternetCredentials(
"notesnook",
username,
password,
KEYSTORE_CONFIG
export async function clearAppLockVerificationCipher() {
CipherStorage.removeItem(APPLOCK_CIPHER);
}
export async function validateAppLockPassword(appLockPassword) {
try {
const appLockCipher = CipherStorage.getMap(APPLOCK_CIPHER);
if (!appLockCipher) return true;
const decrypted = await decrypt(
{
password: appLockPassword
},
appLockCipher
);
console.log("IOS KEYCHAIN MIGRATION COMPLETED!");
MMKV.setBool(IOS_KEYCHAIN_UPGRAGE_KEY, true);
DatabaseLogger.info(
`validateAppLockPassword: ${typeof decrypted === "string"}`
);
return typeof decrypted === "string";
} catch (e) {
DatabaseLogger.error(e);
return false;
}
}
let DB_KEY;
export function clearDatabaseKey() {
DB_KEY = undefined;
DatabaseLogger.info("Cleared database key");
}
export async function getDatabaseKey(appLockPassword) {
if (DB_KEY) return DB_KEY;
try {
if (appLockPassword) {
const databaseKeyCipher = CipherStorage.getMap("databaseKeyCipher");
const databaseKey = await decrypt(
{
password: appLockPassword
},
databaseKeyCipher
);
DatabaseLogger.info("Getting database key from cipher");
DB_KEY = databaseKey;
}
if (!DB_KEY) {
const hasKey = await Keychain.hasInternetCredentials(
KEYCHAIN_SERVER_DBKEY
);
if (hasKey) {
let credentials = await Keychain.getInternetCredentials(
KEYCHAIN_SERVER_DBKEY,
KEYSTORE_CONFIG
);
DatabaseLogger.info("Getting database key from Keychain");
DB_KEY = credentials.password;
}
}
if (!DB_KEY) {
DatabaseLogger.info("Generating new database key");
const password = generatePassword();
const derivedDatabaseKey = await Sodium.deriveKey(
password,
NOTESNOOK_DB_KEY_SALT
);
DB_KEY = derivedDatabaseKey.key;
await Keychain.setInternetCredentials(
KEYCHAIN_SERVER_DBKEY,
"notesnook",
DB_KEY,
KEYSTORE_CONFIG
);
}
if (await Keychain.hasInternetCredentials("notesnook")) {
const userKeyCredentials = await Keychain.getInternetCredentials(
"notesnook",
KEYSTORE_CONFIG
);
if (userKeyCredentials) {
const userKeyCipher = await encrypt(
{
key: DB_KEY,
salt: NOTESNOOK_USER_KEY_SALT
},
userKeyCredentials.password
);
// Store encrypted user key in MMKV
MMKV.setMap(USER_KEY_CIPHER, userKeyCipher);
await Keychain.resetInternetCredentials("notesnook");
}
DatabaseLogger.info("Migrated user credentials to cipher storage");
}
return DB_KEY;
} catch (e) {
console.log(e, "error");
DatabaseLogger.error(e);
return null;
}
}
export async function deriveCryptoKey(data) {
try {
let credentials = await Sodium.deriveKey(data.password, data.salt);
const userKeyCipher = await encrypt(
{
key: await getDatabaseKey(),
salt: NOTESNOOK_USER_KEY_SALT
},
credentials.key
);
DatabaseLogger.info("User key stored: ", !!userKeyCipher);
// Store encrypted user key in MMKV
MMKV.setMap(USER_KEY_CIPHER, userKeyCipher);
return credentials.key;
} catch (e) {
DatabaseLogger.error(e);
}
}
export async function getCryptoKey(_name) {
try {
if (await Keychain.hasInternetCredentials("notesnook")) {
let credentials = await Keychain.getInternetCredentials(
"notesnook",
KEYSTORE_CONFIG
);
// upgrades ios keychain to use accessGroups
// so we have access to keychain in share extension.
await upgradeIOSKeychain(credentials.username, credentials.password);
return credentials.password;
} else {
const keyCipher = MMKV.getMap(USER_KEY_CIPHER);
if (!keyCipher) {
DatabaseLogger.info("User key cipher is null");
return null;
}
const key = await decrypt(
{
key: await getDatabaseKey()
},
keyCipher
);
DatabaseLogger.info("User key decrypted: ", !!key);
return key;
} catch (e) {
console.error(e);
console.log("getCryptoKey", e);
DatabaseLogger.error(e);
}
}
export async function removeCryptoKey(_name) {
try {
let result = await Keychain.resetInternetCredentials("notesnook");
return result;
MMKV.removeItem(USER_KEY_CIPHER);
await Keychain.resetInternetCredentials("notesnook");
return true;
} catch (e) {
console.error(e);
DatabaseLogger.error(e);
}
}
@@ -109,7 +303,7 @@ export async function generateCryptoKey(password, salt) {
let credentials = await Sodium.deriveKey(password, salt || null);
return credentials;
} catch (e) {
console.log("generateCryptoKey: ", e);
DatabaseLogger.error(e);
}
}
@@ -123,9 +317,29 @@ export async function decrypt(password, data) {
return undefined;
let _data = { ...data };
_data.output = "plain";
if (!password.salt) password.salt = data.salt;
return await Sodium.decrypt(password, _data);
}
export async function decryptMulti(password, data) {
if (!password.password && !password.key) return undefined;
if (password.password && password.password === "" && !password.key)
return undefined;
data = data.map((d) => {
d.output = "plain";
return d;
});
if (data.length && !password.salt) {
password.salt = data[0].salt;
}
return await Sodium.decryptMulti(password, data);
}
export function parseAlgorithm(alg) {
if (!alg) return {};
const [enc, kdf, compressed, compressionAlg, base64variant] = alg.split("-");
@@ -154,3 +368,24 @@ export async function encrypt(password, data) {
alg: getAlgorithm(7)
};
}
export async function encryptMulti(password, data) {
if (!password.password && !password.key) return undefined;
if (password.password && password.password === "" && !password.key)
return undefined;
let results = await Sodium.encryptMulti(
password,
data.map((item) => ({
type: "plain",
data: item
}))
);
return !results
? []
: results.map((result) => ({
...result,
alg: getAlgorithm(7)
}));
}

View File

@@ -16,57 +16,73 @@ 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 "./logger";
import { database } from "@notesnook/common";
import { initalize, logger as dbLogger } from "@notesnook/core/logger";
import { logger as dbLogger } from "@notesnook/core/dist/logger";
import { Platform } from "react-native";
import { MMKVLoader } from "react-native-mmkv-storage";
import filesystem from "../filesystem";
import * as Gzip from "react-native-gzip";
import EventSource from "../../utils/sse/even-source-ios";
import AndroidEventSource from "../../utils/sse/event-source";
import Storage, { KV } from "./storage";
import * as Gzip from "react-native-gzip";
import { SqliteAdapter, SqliteIntrospector, SqliteQueryCompiler } from "kysely";
import filesystem from "../filesystem";
import Storage from "./storage";
import { RNSqliteDriver } from "./sqlite.kysely";
import { getDatabaseKey } from "./encryption";
const LoggerStorage = new MMKVLoader()
.withInstanceID("notesnook_logs")
.initialize();
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"
}
);
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.8.101:5264",
// AUTH_HOST: "http://192.168.8.101:8264",
// SSE_HOST: "http://192.168.8.101:7264",
// SUBSCRIPTIONS_HOST: "http://192.168.8.101:9264",
// ISSUES_HOST: "http://192.168.8.101: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"
}
);
export async function setupDatabase(password) {
const key = await getDatabaseKey(password);
if (!key)
throw new Error("Database setup failed, could not get database key");
database.setup(
Storage,
Platform.OS === "ios" ? EventSource : AndroidEventSource,
filesystem,
{
console.log("Opening database with key:", !!key);
database.setup({
storage: Storage,
eventsource: Platform.OS === "ios" ? EventSource : AndroidEventSource,
fs: filesystem,
compressor: {
compress: Gzip.deflate,
decompress: Gzip.inflate
},
batchSize: 100,
sqliteOptions: {
dialect: (name) => ({
createDriver: () => {
return new RNSqliteDriver({ async: true, dbName: name });
},
createAdapter: () => new SqliteAdapter(),
createIntrospector: (db) => new SqliteIntrospector(db),
createQueryCompiler: () => new SqliteQueryCompiler()
}),
tempStore: "memory",
journalMode: Platform.OS === "ios" ? "DELETE" : "WAL",
password: key
}
);
initalize(new KV(LoggerStorage), true);
});
}
export const db = database;
export const DatabaseLogger = dbLogger;

View File

@@ -0,0 +1,29 @@
/*
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 { MMKVLoader } from "react-native-mmkv-storage";
import { initialize } from "@notesnook/core/dist/logger";
import { KV } from "./storage";
const LoggerStorage = new MMKVLoader()
.withInstanceID("notesnook_logs")
.initialize();
initialize(new KV(LoggerStorage));
export { LoggerStorage };

View File

@@ -18,12 +18,13 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { Platform } from "react-native";
import MMKVStorage, { ProcessingModes } from "react-native-mmkv-storage";
import { ProcessingModes, MMKVLoader } from "react-native-mmkv-storage";
export const MMKV = new MMKVStorage.Loader()
export const MMKV = new MMKVLoader()
.setProcessingMode(
Platform.OS === "ios"
? ProcessingModes.MULTI_PROCESS
: ProcessingModes.SINGLE_PROCESS
)
.disableIndexing()
.initialize();

View File

@@ -0,0 +1,123 @@
/*
This file is part of the Notesnook project (https://notesnook.com/)
Copyright (C) 2023 Streetwriters (Private) Limited
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import type { DatabaseConnection, Driver, QueryResult } from "kysely";
import { CompiledQuery } from "kysely";
import { QuickSQLiteConnection, open } from "react-native-quick-sqlite";
type Config = { dbName: string; async: boolean; location: string };
export class RNSqliteDriver implements Driver {
private connection?: DatabaseConnection;
private connectionMutex = new ConnectionMutex();
private db: QuickSQLiteConnection;
constructor(private readonly config: Config) {
this.db = open({
name: config.dbName
});
}
async init(): Promise<void> {
this.connection = new RNSqliteConnection(this.db);
}
async acquireConnection(): Promise<DatabaseConnection> {
// SQLite only has one single connection. We use a mutex here to wait
// until the single connection has been released.
await this.connectionMutex.lock();
return this.connection!;
}
async beginTransaction(connection: DatabaseConnection): Promise<void> {
await connection.executeQuery(CompiledQuery.raw("BEGIN TRANSACTION"));
}
async commitTransaction(connection: DatabaseConnection): Promise<void> {
await connection.executeQuery(CompiledQuery.raw("COMMIT"));
}
async rollbackTransaction(connection: DatabaseConnection): Promise<void> {
await connection.executeQuery(CompiledQuery.raw("ROLLBACK"));
}
async releaseConnection(): Promise<void> {
this.connectionMutex.unlock();
}
async destroy(): Promise<void> {
this.db.close();
}
}
class ConnectionMutex {
private promise?: Promise<void>;
private resolve?: () => void;
async lock(): Promise<void> {
while (this.promise) {
await this.promise;
}
this.promise = new Promise((resolve) => {
this.resolve = resolve;
});
}
unlock(): void {
const resolve = this.resolve;
this.promise = undefined;
this.resolve = undefined;
resolve?.();
}
}
class RNSqliteConnection implements DatabaseConnection {
constructor(private readonly db: QuickSQLiteConnection) {}
streamQuery<R>(): AsyncIterableIterator<QueryResult<R>> {
throw new Error("wasqlite driver doesn't support streaming");
}
async executeQuery<R>(
compiledQuery: CompiledQuery<unknown>
): Promise<QueryResult<R>> {
const { parameters, sql, query } = compiledQuery;
const mode =
query.kind === "SelectQueryNode"
? "query"
: query.kind === "RawNode"
? "raw"
: "exec";
const result = await this.db.executeAsync(sql, parameters as any[]);
if (mode === "query" || !result.insertId)
return {
rows: result.rows?._array || []
};
return {
insertId: BigInt(result.insertId),
numAffectedRows: BigInt(result.rowsAffected),
rows: mode === "raw" ? result.rows?._array || [] : []
};
}
}

View File

@@ -27,11 +27,16 @@ import {
getCryptoKey,
getRandomBytes,
hash,
removeCryptoKey
removeCryptoKey,
decryptMulti,
encryptMulti
} from "./encryption";
import { MMKV } from "./mmkv";
export class KV {
/**
* @type {typeof MMKV}
*/
storage = null;
constructor(storage) {
this.storage = storage;
@@ -53,6 +58,7 @@ export class KV {
key,
typeof data === "string" ? data : JSON.stringify(data)
);
return true;
}
@@ -60,27 +66,37 @@ export class KV {
if (keys.length <= 0) {
return [];
} else {
let data = await this.storage.getMultipleItemsAsync(keys.slice());
return data.map(([key, value]) => {
let obj;
try {
obj = JSON.parse(value);
} catch (e) {
obj = value;
}
return [key, obj];
});
try {
let data = await this.storage.getMultipleItemsAsync(
keys.slice(),
"string"
);
return data.map(([key, value]) => {
let obj;
try {
obj = JSON.parse(value);
} catch (e) {
obj = value;
}
return [key, obj];
});
} catch (e) {
console.log(e);
}
}
}
async remove(key) {
return await this.storage.removeItem(key);
return this.storage.removeItem(key);
}
async removeMulti(keys) {
if (!keys) return true;
return this.storage.removeItems(keys);
}
async clear() {
return await this.storage.clearStore();
return this.storage.clearStore();
}
async getAllKeys() {
@@ -96,6 +112,10 @@ export class KV {
);
return keys;
}
async writeMulti(items) {
return this.storage.setMultipleItemsAsync(items, "object");
}
}
const DefaultStorage = new KV(MMKV);
@@ -129,8 +149,11 @@ export default {
remove: (key) => DefaultStorage.remove(key),
clear: () => DefaultStorage.clear(),
getAllKeys: () => DefaultStorage.getAllKeys(),
writeMulti: (items) => DefaultStorage.writeMulti(items),
removeMulti: (keys) => DefaultStorage.removeMulti(keys),
encrypt,
decrypt,
decryptMulti,
getRandomBytes,
checkAndCreateDir,
requestPermission,
@@ -138,5 +161,6 @@ export default {
getCryptoKey,
removeCryptoKey,
hash,
generateCryptoKey
generateCryptoKey,
encryptMulti
};

View File

@@ -22,18 +22,20 @@ import {
getFileNameWithExtension,
isImage,
isDocument
} from "@notesnook/core/utils/filename";
} from "@notesnook/core/dist/utils/filename";
import React from "react";
import { Platform } from "react-native";
import * as ScopedStorage from "react-native-scoped-storage";
import { subscribe, zip } from "react-native-zip-archive";
import RNFetchBlob from "react-native-blob-util";
import { ShareComponent } from "../../components/sheets/export-notes/share";
import { ToastEvent, presentSheet } from "../../services/event-manager";
import { ToastManager, presentSheet } from "../../services/event-manager";
import { useAttachmentStore } from "../../stores/use-attachment-store";
import { db } from "../database";
import Storage from "../database/storage";
import { cacheDir, copyFileAsync, releasePermissions } from "./utils";
import { createCacheDir, exists } from "./io";
import { IOS_APPGROUPID } from "../../utils/constants";
export const FileDownloadStatus = {
Success: 1,
@@ -44,15 +46,15 @@ export const FileDownloadStatus = {
* Download all user's attachments
* @returns
*/
export function downloadAllAttachments() {
const attachments = db.attachments.all;
export async function downloadAllAttachments() {
const attachments = await db.attachments.all.ids();
return downloadAttachments(attachments);
}
/**
* Downloads provided attachments to a .zip file
* on user's device.
* @param attachments
* @param {string[]} attachments
* @param onProgress
* @returns
*/
@@ -62,6 +64,7 @@ export async function downloadAttachments(
canceled,
groupId
) {
await createCacheDir();
if (!attachments || !attachments.length) return;
const result = new Map();
@@ -86,8 +89,8 @@ export async function downloadAttachments(
await RNFetchBlob.fs.mkdir(zipSourceFolder);
for (let i = 0; i < attachments.length; i++) {
let attachment = attachments[i];
const hash = attachment.metadata.hash;
let attachment = await db.attachments.attachment(attachments[i]);
const hash = attachment.hash;
try {
if (canceled.current) {
RNFetchBlob.fs.unlink(zipSourceFolder).catch(console.log);
@@ -111,15 +114,16 @@ export async function downloadAttachments(
}
if (!uri) throw new Error("Failed to download file");
// Move file to the source folder we will zip eventually and rename the file to it's actual name.
const filePath = `${zipSourceFolder}/${attachment.metadata.filename}`;
const filePath = `${zipSourceFolder}/${attachment.filename}`;
await RNFetchBlob.fs.mv(`${cacheDir}/${uri}`, filePath);
result.set(hash, {
filename: attachment.metadata.filename,
status: FileDownloadStatus.Success
filename: attachment.filename,
status: FileDownloadStatus.Success,
attachment: attachment
});
} catch (e) {
result.set(hash, {
filename: attachment.metadata.filename,
filename: attachment.filename,
status: FileDownloadStatus.Fail,
reason: e
});
@@ -179,10 +183,14 @@ export default async function downloadAttachment(
silent: false,
cache: false,
throwError: false,
groupId: undefined
groupId: undefined,
base64: false,
text: false
}
) {
let attachment = db.attachments.attachment(hash);
await createCacheDir();
let attachment = await db.attachments.attachment(hash);
if (!attachment) {
console.log("attachment not found");
return;
@@ -201,35 +209,41 @@ export default async function downloadAttachment(
try {
console.log(
"starting download attachment",
attachment.metadata.hash,
attachment.hash,
options.groupId
);
await db.fs.downloadFile(
options.groupId || attachment.metadata.hash,
attachment.metadata.hash
);
if (
!(await RNFetchBlob.fs.exists(`${cacheDir}/${attachment.metadata.hash}`))
)
await db
.fs()
.downloadFile(options.groupId || attachment.hash, attachment.hash);
if (!(await exists(attachment.hash))) {
return;
}
if (options.base64 || options.text) {
return await db.attachments.read(
attachment.hash,
options.base64 ? "base64" : "text"
);
}
let filename = getFileNameWithExtension(
attachment.metadata.filename,
attachment.metadata.type
attachment.filename,
attachment.mimeType
);
let key = await db.attachments.decryptKey(attachment.key);
let info = {
iv: attachment.iv,
salt: attachment.salt,
length: attachment.length,
length: attachment.size,
alg: attachment.alg,
hash: attachment.metadata.hash,
hashType: attachment.metadata.hashType,
mime: attachment.metadata.type,
hash: attachment.hash,
hashType: attachment.hashType,
mime: attachment.mimeType,
fileName: options.cache ? undefined : filename,
uri: options.cache ? undefined : folder.uri,
chunkSize: attachment.chunkSize
chunkSize: attachment.chunkSize,
appGroupId: IOS_APPGROUPID
};
let fileUri = await Sodium.decryptFile(
@@ -239,7 +253,7 @@ export default async function downloadAttachment(
);
if (!options.silent) {
ToastEvent.show({
ToastManager.show({
heading: "Download successful",
message: filename + " downloaded",
type: "success"
@@ -248,11 +262,11 @@ export default async function downloadAttachment(
if (
attachment.dateUploaded &&
!isImage(attachment.metadata?.type) &&
!isDocument(attachment.metadata?.type)
!isImage(attachment.mimeType) &&
!isDocument(attachment.mimeType)
) {
RNFetchBlob.fs
.unlink(RNFetchBlob.fs.dirs.CacheDir + `/${attachment.metadata.hash}`)
.unlink(RNFetchBlob.fs.dirs.CacheDir + `/${attachment.hash}`)
.catch(console.log);
}
if (Platform.OS === "ios" && !options.cache) {
@@ -267,7 +281,7 @@ export default async function downloadAttachment(
: "File Manager/Notesnook/downloads"
}`,
icon: "download",
context: global ? null : attachment.metadata.hash,
context: global ? null : attachment.hash,
component: <ShareComponent uri={fileUri} name={filename} padding={12} />
});
}
@@ -277,10 +291,13 @@ export default async function downloadAttachment(
console.log("download attachment error: ", e);
if (attachment.dateUploaded) {
RNFetchBlob.fs
.unlink(RNFetchBlob.fs.dirs.CacheDir + `/${attachment.metadata.hash}`)
.unlink(RNFetchBlob.fs.dirs.CacheDir + `/${attachment.hash}`)
.catch(console.log);
RNFetchBlob.fs
.unlink(RNFetchBlob.fs.dirs.CacheDir + `/${attachment.hash}_dcache`)
.catch(console.log);
}
useAttachmentStore.getState().remove(attachment.metadata.hash);
useAttachmentStore.getState().remove(attachment.hash);
if (options.throwError) {
throw e;
}

View File

@@ -17,22 +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 hosts from "@notesnook/core/utils/constants";
import hosts from "@notesnook/core/dist/utils/constants";
import NetInfo from "@react-native-community/netinfo";
import RNFetchBlob from "react-native-blob-util";
import { ToastEvent } from "../../services/event-manager";
import { ToastManager } from "../../services/event-manager";
import { useAttachmentStore } from "../../stores/use-attachment-store";
import { db } from "../database";
import { cacheDir, fileCheck } from "./utils";
import { createCacheDir, exists } from "./io";
export async function downloadFile(filename, data, cancelToken) {
if (!data) return false;
let { url, headers } = data;
await createCacheDir();
let { url, headers } = data;
let path = `${cacheDir}/${filename}`;
try {
let exists = await RNFetchBlob.fs.exists(path);
if (exists) {
if (await exists(filename)) {
return true;
}
@@ -60,44 +63,55 @@ export async function downloadFile(filename, data, cancelToken) {
console.log("downloading: ", recieved, total);
});
cancelToken.cancel = request.cancel;
cancelToken.cancel = () => {
useAttachmentStore.getState().remove(filename);
request.cancel();
};
let response = await request;
await fileCheck(response, totalSize);
let status = response.info().status;
useAttachmentStore.getState().remove(filename);
return status >= 200 && status < 300;
} catch (e) {
ToastEvent.show({
heading: "Error downloading file",
message: e.message,
type: "error",
context: "global"
});
ToastEvent.show({
heading: "Error downloading file",
message: e.message,
type: "error",
context: "local"
});
if (e.message !== "canceled") {
ToastManager.show({
heading: "Error downloading file",
message: e.message,
type: "error",
context: "global"
});
ToastManager.show({
heading: "Error downloading file",
message: e.message,
type: "error",
context: "local"
});
}
useAttachmentStore.getState().remove(filename);
RNFetchBlob.fs.unlink(path).catch(console.log);
console.log("download file error: ", e, url, headers);
console.log("Download file error:", e, url, headers);
return false;
}
}
export async function getUploadedFileSize(hash) {
const url = `${hosts.API_HOST}/s3?name=${hash}`;
const token = await db.user.tokenManager.getAccessToken();
try {
const url = `${hosts.API_HOST}/s3?name=${hash}`;
const token = await db.tokenManager.getAccessToken();
const attachmentInfo = await fetch(url, {
method: "HEAD",
headers: { Authorization: `Bearer ${token}` }
});
const attachmentInfo = await fetch(url, {
method: "HEAD",
headers: { Authorization: `Bearer ${token}` }
});
const contentLength = parseInt(attachmentInfo.headers?.get("content-length"));
return isNaN(contentLength) ? 0 : contentLength;
const contentLength = parseInt(
attachmentInfo.headers?.get("content-length")
);
return isNaN(contentLength) ? 0 : contentLength;
} catch (e) {
return 0;
}
}
export async function checkAttachment(hash) {
@@ -105,7 +119,7 @@ export async function checkAttachment(hash) {
const isInternetReachable =
internetState.isConnected && internetState.isInternetReachable;
if (!isInternetReachable) return { success: true };
const attachment = db.attachments.attachment(hash);
const attachment = await db.attachments.attachment(hash);
if (!attachment) return { failed: "Attachment not found." };
try {

View File

@@ -20,32 +20,25 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
import { Platform } from "react-native";
import Sodium from "@ammarahmed/react-native-sodium";
import RNFetchBlob from "react-native-blob-util";
import { cacheDir, getRandomId } from "./utils";
import { cacheDir, cacheDirOld, getRandomId } from "./utils";
import { db } from "../database";
import { compressToBase64 } from "./compress";
import { IOS_APPGROUPID } from "../../utils/constants";
export async function readEncrypted(filename, key, cipherData) {
await migrateFilesFromCache();
console.log("Read encrypted file...");
let path = `${cacheDir}/${filename}`;
try {
const iosAppGroup =
Platform.OS === "ios"
? await RNFetchBlob.fs.pathForAppGroup(IOS_APPGROUPID)
: null;
const appGroupPath = `${iosAppGroup}/${filename}`;
let exists =
(await RNFetchBlob.fs.exists(path)) ||
(Platform.OS === "ios" && (await RNFetchBlob.fs.exists(appGroupPath)));
if (!exists) {
if (!(await exists(filename))) {
return false;
}
const attachment = db.attachments.attachment(filename);
const isPng = /(png)/g.test(attachment?.metadata.type);
const isJpeg = /(jpeg|jpg)/g.test(attachment?.metadata.type);
const attachment = await db.attachments.attachment(filename);
const isPng = /(png)/g.test(attachment?.mimeType || "");
const isJpeg = /(jpeg|jpg)/g.test(attachment?.mimeType || "");
console.log("decrypting....");
let output = await Sodium.decryptFile(
key,
{
@@ -59,6 +52,7 @@ export async function readEncrypted(filename, key, cipherData) {
: "base64"
: "text"
);
console.log("file decrypted...");
if (cipherData.outputType === "base64" && (isPng || isJpeg)) {
const dCachePath = `${cacheDir}/${output}`;
output = await compressToBase64(
@@ -70,7 +64,7 @@ export async function readEncrypted(filename, key, cipherData) {
return output;
} catch (e) {
RNFetchBlob.fs.unlink(path).catch(console.log);
console.log(e);
console.log("readEncrypted", e);
return false;
}
}
@@ -88,6 +82,7 @@ export async function hashBase64(data) {
}
export async function writeEncryptedBase64({ data, key }) {
await createCacheDir();
let filepath = cacheDir + `/${getRandomId("imagecache_")}`;
await RNFetchBlob.fs.writeFile(filepath, data, "base64");
let output = await Sodium.encryptFile(key, {
@@ -103,6 +98,7 @@ export async function writeEncryptedBase64({ data, key }) {
}
export async function deleteFile(filename, data) {
await createCacheDir();
let delFilePath = cacheDir + `/${filename}`;
if (!data) {
if (!filename) return;
@@ -128,19 +124,89 @@ export async function deleteFile(filename, data) {
export async function clearFileStorage() {
try {
let files = await RNFetchBlob.fs.ls(cacheDir);
let oldCache = await RNFetchBlob.fs.ls(cacheDirOld);
for (let file of files) {
try {
await RNFetchBlob.fs.unlink(cacheDir + `/${file}`);
} catch (e) {
console.log(e);
}
await RNFetchBlob.fs.unlink(cacheDir + `/${file}`).catch(console.log);
}
for (let file of oldCache) {
await RNFetchBlob.fs.unlink(cacheDirOld + `/${file}`).catch(console.log);
}
} catch (e) {
console.log(e);
console.log("clearFileStorage", e);
}
}
export async function createCacheDir() {
if (!(await RNFetchBlob.fs.exists(cacheDir))) {
await RNFetchBlob.fs.mkdir(cacheDir);
console.log("Cache directory created");
}
}
export async function migrateFilesFromCache() {
try {
await createCacheDir();
const migratedFilesPath = cacheDir + "/.migrated_1";
const migrated = await RNFetchBlob.fs.exists(migratedFilesPath);
if (migrated) {
console.log("Files migrated already");
return;
}
let files = await RNFetchBlob.fs.ls(cacheDir);
console.log("Files to migrate:", files.join(","));
let oldCache = await RNFetchBlob.fs.ls(cacheDirOld);
for (let file of oldCache) {
if (file.startsWith("org.") || file.startsWith("com.")) continue;
RNFetchBlob.fs
.mv(cacheDirOld + `/${file}`, cacheDir + `/${file}`)
.catch(console.log);
console.log("Moved", file);
}
await RNFetchBlob.fs.createFile(migratedFilesPath, "1", "utf8");
} catch (e) {
console.log("migrateFilesFromCache", e);
}
}
const ABYTES = 17;
export async function exists(filename) {
let exists = await RNFetchBlob.fs.exists(`${cacheDir}/${filename}`);
let path = `${cacheDir}/${filename}`;
const iosAppGroup =
Platform.OS === "ios"
? await RNFetchBlob.fs.pathForAppGroup(IOS_APPGROUPID)
: null;
const appGroupPath = `${iosAppGroup}/${filename}`;
let exists = await RNFetchBlob.fs.exists(path);
// Check if file is present in app group path.
let existsInAppGroup = false;
if (!exists && Platform.OS === "ios") {
existsInAppGroup = await RNFetchBlob.fs.exists(appGroupPath);
}
if (exists || existsInAppGroup) {
const attachment = await db.attachments.attachment(filename);
const totalChunks = Math.ceil(attachment.size / attachment.chunkSize);
const totalAbytes = totalChunks * ABYTES;
const expectedFileSize = attachment.size + totalAbytes;
const stat = await RNFetchBlob.fs.stat(
existsInAppGroup ? appGroupPath : path
);
if (stat.size !== expectedFileSize) {
RNFetchBlob.fs
.unlink(existsInAppGroup ? appGroupPath : path)
.catch(console.log);
return false;
}
exists = true;
}
return exists;
}

View File

@@ -21,14 +21,15 @@ import RNFetchBlob from "react-native-blob-util";
import { useAttachmentStore } from "../../stores/use-attachment-store";
import { DatabaseLogger, db } from "../database";
import { cacheDir } from "./utils";
import { isImage, isDocument } from "@notesnook/core/utils/filename";
import { isImage, isDocument } from "@notesnook/core/dist/utils/filename";
import { Platform } from "react-native";
import { IOS_APPGROUPID } from "../../utils/constants";
import { createCacheDir } from "./io";
export async function uploadFile(filename, data, cancelToken) {
if (!data) return false;
let { url, headers } = data;
await createCacheDir();
DatabaseLogger.info(`Preparing to upload file: ${filename}`);
try {
@@ -72,7 +73,10 @@ export async function uploadFile(filename, data, cancelToken) {
);
});
cancelToken.cancel = request.cancel;
cancelToken.cancel = () => {
useAttachmentStore.getState().remove(filename);
request.cancel();
};
let response = await request;
let status = response.info().status;
@@ -83,12 +87,9 @@ export async function uploadFile(filename, data, cancelToken) {
DatabaseLogger.info(
`File upload status: ${filename}, ${status}, ${text}`
);
let attachment = db.attachments.attachment(filename);
let attachment = await db.attachments.attachment(filename);
if (!attachment) return result;
if (
!isImage(attachment.metadata.type) &&
!isDocument(attachment.metadata?.type)
) {
if (!isImage(attachment.mimeType) && !isDocument(attachment.mimeType)) {
RNFetchBlob.fs.unlink(`${cacheDir}/${filename}`).catch(console.log);
}
} else {

View File

@@ -21,7 +21,12 @@ import * as ScopedStorage from "react-native-scoped-storage";
import { Platform } from "react-native";
import RNFetchBlob from "react-native-blob-util";
export const cacheDir = RNFetchBlob.fs.dirs.CacheDir;
export const cacheDirOld = RNFetchBlob.fs.dirs.CacheDir;
export const cacheDir =
Platform.OS == "ios"
? RNFetchBlob.fs.dirs.LibraryDir + "/.cache"
: RNFetchBlob.fs.dirs.DocumentDir + "/.cache";
export function getRandomId(prefix) {
return Math.random()
@@ -66,9 +71,12 @@ export function cancelable(operation) {
}
export function copyFileAsync(source, dest) {
return new Promise((resolve) => {
return new Promise((resolve, reject) => {
ScopedStorage.copyFile(source, dest, (e, r) => {
console.log(e, r);
if (e) {
reject(e);
return;
}
resolve();
});
});

View File

@@ -17,22 +17,22 @@ 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 from "react";
import { FlatList, View } from "react-native";
import { useSelectionStore } from "../../stores/use-selection-store";
import { View } from "react-native";
import { useMessageStore } from "../../stores/use-message-store";
import { useThemeStore } from "../../stores/use-theme-store";
import { useSelectionStore } from "../../stores/use-selection-store";
import { allowedOnPlatform, renderItem } from "./functions";
export const Announcement = ({ color }) => {
const colors = useThemeStore((state) => state.colors);
const { colors } = useThemeColors();
const announcements = useMessageStore((state) => state.announcements);
let announcement = announcements.length > 0 ? announcements[0] : null;
const selectionMode = useSelectionStore((state) => state.selectionMode);
return !announcement || selectionMode ? null : (
<View
style={{
backgroundColor: colors.bg,
backgroundColor: colors.primary.background,
width: "100%",
paddingHorizontal: 12,
paddingTop: 12,
@@ -44,28 +44,26 @@ export const Announcement = ({ color }) => {
width: "100%",
borderRadius: 10,
overflow: "hidden",
backgroundColor: colors.nav,
backgroundColor: colors.secondary.background,
paddingBottom: 12
}}
>
<View>
<FlatList
style={{
width: "100%",
marginTop: 12
}}
data={announcement?.body.filter((item) =>
allowedOnPlatform(item.platforms)
)}
renderItem={({ item, index }) =>
<View
style={{
width: "100%",
marginTop: 12
}}
>
{announcement?.body
.filter((item) => allowedOnPlatform(item.platforms))
.map((item, index) =>
renderItem({
item: item,
index: index,
color: colors[color],
inline: true
})
}
/>
)}
</View>
</View>
</View>

View File

@@ -22,7 +22,7 @@ import { Linking, View } from "react-native";
//import SettingsBackupAndRestore from '../../screens/settings/backup-restore';
import { eSendEvent, presentSheet } from "../../services/event-manager";
import Sync from "../../services/sync";
import { useThemeStore } from "../../stores/use-theme-store";
import { useThemeColors } from "@notesnook/theme";
import { eCloseAnnouncementDialog, eCloseSheet } from "../../utils/events";
import { SIZE } from "../../utils/size";
import { sleep } from "../../utils/time";
@@ -33,7 +33,7 @@ import { Button } from "../ui/button";
import { allowedOnPlatform, getStyle } from "./functions";
export const Cta = ({ actions, style = {}, color, inline }) => {
const colors = useThemeStore((state) => state.colors);
const { colors } = useThemeColors();
let buttons =
actions.filter((item) => allowedOnPlatform(item.platforms)) || [];
@@ -92,8 +92,7 @@ export const Cta = ({ actions, style = {}, color, inline }) => {
style={{
height: 30,
alignSelf: "flex-start",
paddingHorizontal: 0,
marginTop: -6
paddingHorizontal: 0
}}
/>
))}
@@ -111,7 +110,6 @@ export const Cta = ({ actions, style = {}, color, inline }) => {
style={{
alignSelf: "flex-start",
paddingHorizontal: 0,
marginTop: -6,
marginLeft: 12
}}
textStyle={{
@@ -129,9 +127,11 @@ export const Cta = ({ actions, style = {}, color, inline }) => {
title={item.title}
fontSize={SIZE.md}
buttonType={{
color: color ? color : colors.accent,
text: colors.light,
selected: color ? color : colors.accent,
color: color ? color : colors.primary.accent,
text: color
? colors.static.white
: colors.primary.accentForeground,
selected: color ? color : colors.primary.accent,
opacity: 1
}}
onPress={() => onPress(item)}

View File

@@ -25,7 +25,7 @@ import {
eUnSubscribeEvent
} from "../../services/event-manager";
import { useMessageStore } from "../../stores/use-message-store";
import { useThemeStore } from "../../stores/use-theme-store";
import { useThemeColors } from "@notesnook/theme";
import {
eCloseAnnouncementDialog,
eOpenAnnouncementDialog
@@ -34,10 +34,69 @@ import BaseDialog from "../dialog/base-dialog";
import { allowedOnPlatform, renderItem } from "./functions";
import { useCallback } from "react";
/**
* Test announcement
* {
id: "some-announcement",
type: "dialog",
body: [
{
type: "title",
text: "This is a title",
platforms: ["all"]
},
{
type: "description",
text: "Most of you are too busy to keep up to date with what's happening in Notesnook. That is unfortunate because Notesnook has come a looooong way.",
style: {
marginBottom: 1
},
platforms: ["all"]
},
{
type: "description",
text: "To solve this, we are launching the Notesnook Digest — a newsletter to help you stay updated about Notesnook development. And to keep things interesting I'll also sprinkle this newsletter with other interesting stuff like privacy tips & news, interesting books, things I am looking forward to etc.",
style: {
marginBottom: 1
},
platforms: ["all"]
},
{
type: "description",
text: "So be sure to subscribe. There won't be a proper schedule to this (yet) maybe once or twice a month. I promise no spam — only more awesomeness.",
style: {
marginBottom: 1
},
platforms: ["all"]
},
{
type: "description",
text: "— May privacy reign.",
style: {
marginBottom: 1
},
platforms: ["all"]
},
{
type: "callToActions",
actions: [
{
type: "promo",
title: "15% Off",
platforms: ["android"],
data: "com.streetwriters.notesnook.sub.yr.15"
}
],
platforms: ["all"]
}
]
}
*/
export const AnnouncementDialog = () => {
const colors = useThemeStore((state) => state.colors);
const { colors } = useThemeColors();
const [visible, setVisible] = useState(false);
const [info, setInfo] = useState(null);
const [info, setInfo] = useState();
const remove = useMessageStore((state) => state.remove);
useEffect(() => {
@@ -51,7 +110,9 @@ export const AnnouncementDialog = () => {
const open = (data) => {
setInfo(data);
setVisible(true);
setImmediate(() => {
setVisible(true);
});
};
const close = useCallback(() => {
@@ -73,7 +134,7 @@ export const AnnouncementDialog = () => {
<View
style={{
width: DDS.isTab ? 600 : "100%",
backgroundColor: colors.bg,
backgroundColor: colors.primary.background,
maxHeight: DDS.isTab ? "90%" : "100%",
borderRadius: DDS.isTab ? 10 : 0,
overflow: "hidden",

View File

@@ -1,215 +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 React, { useCallback, useEffect, useRef } from "react";
import { Platform, View } from "react-native";
import { enabled } from "react-native-privacy-snapshot";
import { db } from "../../common/database";
import { useAppState } from "../../hooks/use-app-state";
import BiometricService from "../../services/biometrics";
import { useSettingStore } from "../../stores/use-setting-store";
import { useThemeStore } from "../../stores/use-theme-store";
import { useUserStore } from "../../stores/use-user-store";
import { NotesnookModule } from "../../utils/notesnook-module";
import { SIZE } from "../../utils/size";
import { Button } from "../ui/button";
import { IconButton } from "../ui/icon-button";
import Input from "../ui/input";
import Seperator from "../ui/seperator";
import Heading from "../ui/typography/heading";
import Paragraph from "../ui/typography/paragraph";
const AppLockedOverlay = () => {
const colors = useThemeStore((state) => state.colors);
const user = useUserStore((state) => state.user);
const appLocked = useUserStore((state) => state.appLocked);
const lockApp = useUserStore((state) => state.lockApp);
const deviceMode = useSettingStore((state) => state.deviceMode);
const passwordInputRef = useRef();
const password = useRef();
const appState = useAppState();
const biometricUnlockAwaitingUserInput = useRef(false);
const onUnlockAppRequested = useCallback(async () => {
if (!(await BiometricService.isBiometryAvailable())) return;
if (Platform.OS === "android") {
const activityName = await NotesnookModule.getActivityName();
if (activityName !== "MainActivity") return;
}
useSettingStore.getState().setRequestBiometrics(true);
let unlocked = await BiometricService.validateUser(
"Unlock to access your notes",
""
);
if (unlocked) {
lockApp(false);
enabled(false);
password.current = null;
}
setTimeout(() => {
biometricUnlockAwaitingUserInput.current = false;
useSettingStore.getState().setRequestBiometrics(false);
}, 1);
}, [lockApp]);
const onSubmit = async () => {
if (!password.current) return;
try {
let unlocked = await db.user.verifyPassword(password.current);
if (unlocked) {
lockApp(false);
enabled(false);
password.current = null;
}
} catch (e) {
console.error(e);
}
};
useEffect(() => {
if (
biometricUnlockAwaitingUserInput.current ||
useUserStore.getState().disableAppLockRequests
)
return;
if (appLocked && appState === "active") {
biometricUnlockAwaitingUserInput.current = true;
onUnlockAppRequested();
}
}, [appState, onUnlockAppRequested, appLocked]);
return (
appLocked && (
<View
style={{
backgroundColor: colors.bg,
width: "100%",
height: "100%",
position: "absolute",
zIndex: 999,
justifyContent: "center"
}}
>
<View
style={{
flex: 1,
justifyContent: "center",
width:
deviceMode !== "mobile"
? "50%"
: Platform.OS == "ios"
? "95%"
: "100%",
paddingHorizontal: 12,
marginBottom: 30,
marginTop: 15,
alignSelf: "center"
}}
>
<IconButton
name="fingerprint"
size={100}
customStyle={{
width: 100,
height: 100,
marginBottom: 20,
marginTop: user ? 0 : 50
}}
onPress={onUnlockAppRequested}
color={colors.border}
/>
<Heading
color={colors.heading}
style={{
alignSelf: "center",
textAlign: "center"
}}
>
Unlock your notes
</Heading>
<Paragraph
style={{
alignSelf: "center",
textAlign: "center",
maxWidth: "90%"
}}
>
{"Please verify it's you"}
</Paragraph>
<Seperator />
<View
style={{
width: "100%",
padding: 12,
backgroundColor: colors.bg
}}
>
{user ? (
<>
<Input
fwdRef={passwordInputRef}
secureTextEntry
placeholder="Enter account password"
onChangeText={(v) => (password.current = v)}
onSubmit={onSubmit}
/>
</>
) : null}
<View
style={{
marginTop: user ? 25 : 25
}}
>
{user ? (
<>
<Button
title="Continue"
type="accent"
onPress={onSubmit}
width={250}
height={45}
style={{
borderRadius: 150,
marginBottom: 10
}}
fontSize={SIZE.md}
/>
</>
) : null}
<Button
title="Unlock with Biometrics"
width={250}
onPress={onUnlockAppRequested}
icon={"fingerprint"}
type="transparent"
/>
</View>
</View>
</View>
</View>
)
);
};
export default AppLockedOverlay;

View File

@@ -0,0 +1,299 @@
/*
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 React, { useCallback, useEffect, useRef } from "react";
import { Platform, TextInput, View } from "react-native";
//@ts-ignore
import { useThemeColors } from "@notesnook/theme";
import { enabled } from "react-native-privacy-snapshot";
import { DatabaseLogger } from "../../common/database";
import {
decrypt,
encrypt,
getCryptoKey,
getDatabaseKey,
setAppLockVerificationCipher,
validateAppLockPassword
} from "../../common/database/encryption";
import { MMKV } from "../../common/database/mmkv";
import { useAppState } from "../../hooks/use-app-state";
import BiometricService from "../../services/biometrics";
import SettingsService from "../../services/settings";
import { useSettingStore } from "../../stores/use-setting-store";
import { useUserStore } from "../../stores/use-user-store";
import { NotesnookModule } from "../../utils/notesnook-module";
import { SIZE } from "../../utils/size";
import { Button } from "../ui/button";
import { IconButton } from "../ui/icon-button";
import Input from "../ui/input";
import Seperator from "../ui/seperator";
import Heading from "../ui/typography/heading";
import Paragraph from "../ui/typography/paragraph";
const getUser = () => {
const user = MMKV.getString("user");
if (user) {
return JSON.parse(user);
}
};
const verifyUserPassword = async (password: string) => {
try {
await getDatabaseKey();
const key = await getCryptoKey();
const user = getUser();
const cipher = await encrypt(
{
key: key,
salt: user.salt
},
"notesnook"
);
const plainText = await decrypt({ password }, cipher);
return plainText === "notesnook";
} catch (e) {
DatabaseLogger.error(e as Error);
return false;
}
};
const AppLockedOverlay = () => {
const { colors } = useThemeColors();
const user = getUser();
const appLocked = useUserStore((state) => state.appLocked);
const lockApp = useUserStore((state) => state.lockApp);
const deviceMode = useSettingStore((state) => state.deviceMode);
const passwordInputRef = useRef<TextInput>(null);
const password = useRef<string>();
const appState = useAppState();
const biometricUnlockAwaitingUserInput = useRef(false);
const keyboardType = useSettingStore(
(state) => state.settings.applockKeyboardType
);
const appLockHasPasswordSecurity = useSettingStore(
(state) => state.settings.appLockHasPasswordSecurity
);
const biometricsAuthEnabled = useSettingStore(
(state) =>
state.settings.biometricsAuthEnabled === true ||
(state.settings.biometricsAuthEnabled === undefined &&
!state.settings.appLockHasPasswordSecurity)
);
const onUnlockAppRequested = useCallback(async () => {
if (
!biometricsAuthEnabled ||
!(await BiometricService.isBiometryAvailable())
)
return;
biometricUnlockAwaitingUserInput.current = true;
if (Platform.OS === "android") {
const activityName = await NotesnookModule.getActivityName();
if (activityName !== "MainActivity") return;
}
useSettingStore.getState().setRequestBiometrics(true);
const unlocked = await BiometricService.validateUser(
"Unlock to access your notes",
""
);
if (unlocked) {
lockApp(false);
enabled(false);
password.current = undefined;
}
biometricUnlockAwaitingUserInput.current = false;
setTimeout(() => {
if (biometricUnlockAwaitingUserInput.current) return;
useSettingStore.getState().setRequestBiometrics(false);
}, 500);
}, [biometricsAuthEnabled, lockApp]);
const onSubmit = async () => {
if (!password.current) return;
try {
const unlocked = appLockHasPasswordSecurity
? validateAppLockPassword(password.current)
: await verifyUserPassword(password.current);
if (unlocked) {
if (!appLockHasPasswordSecurity) {
await setAppLockVerificationCipher(password.current);
SettingsService.set({
appLockHasPasswordSecurity: true,
applockKeyboardType: "default"
});
DatabaseLogger.info("App lock migrated to password security");
}
lockApp(false);
enabled(false);
password.current = undefined;
}
} catch (e) {
console.error(e);
}
};
useEffect(() => {
if (
biometricUnlockAwaitingUserInput.current ||
useUserStore.getState().disableAppLockRequests
)
return;
if (
appLocked &&
appState === "active" &&
!useSettingStore.getState().requestBiometrics
) {
biometricUnlockAwaitingUserInput.current = true;
onUnlockAppRequested();
}
}, [appState, onUnlockAppRequested, appLocked]);
return appLocked ? (
<View
style={{
backgroundColor: colors.primary.background,
width: "100%",
height: "100%",
position: "absolute",
zIndex: 999,
justifyContent: "center"
}}
>
<View
style={{
flex: 1,
justifyContent: "center",
width:
deviceMode !== "mobile"
? "50%"
: Platform.OS == "ios"
? "95%"
: "100%",
paddingHorizontal: 12,
marginBottom: 30,
marginTop: 15,
alignSelf: "center"
}}
>
<IconButton
name="fingerprint"
size={100}
customStyle={{
width: 100,
height: 100,
marginBottom: 20,
marginTop: user ? 0 : 50
}}
onPress={onUnlockAppRequested}
color={colors.primary.border}
/>
<Heading
color={colors.primary.heading}
style={{
alignSelf: "center",
textAlign: "center"
}}
>
Unlock your notes
</Heading>
<Paragraph
style={{
alignSelf: "center",
textAlign: "center",
maxWidth: "90%"
}}
>
{"Please verify it's you"}
</Paragraph>
<Seperator />
<View
style={{
width: "100%",
padding: 12,
backgroundColor: colors.primary.background
}}
>
{user || appLockHasPasswordSecurity ? (
<>
<Input
fwdRef={passwordInputRef}
secureTextEntry
keyboardType={
appLockHasPasswordSecurity ? keyboardType : "default"
}
placeholder={`Enter ${
appLockHasPasswordSecurity
? `app lock ${
keyboardType === "numeric" ? "pin" : "password"
}`
: "account password"
}`}
onChangeText={(v) => (password.current = v)}
onSubmit={() => {
onSubmit();
}}
/>
</>
) : null}
<View
style={{
marginTop: user ? 25 : 25
}}
>
{user || appLockHasPasswordSecurity ? (
<>
<Button
title="Continue"
type="accent"
onPress={onSubmit}
width={250}
height={45}
style={{
borderRadius: 150,
marginBottom: 10
}}
fontSize={SIZE.md}
/>
</>
) : null}
{biometricsAuthEnabled ? (
<Button
title="Unlock with Biometrics"
width={250}
onPress={onUnlockAppRequested}
icon={"fingerprint"}
type="transparent"
/>
) : null}
</View>
</View>
</View>
</View>
) : null;
};
export default AppLockedOverlay;

View File

@@ -17,9 +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 { formatBytes } from "@notesnook/common";
import { Attachment, Note, VirtualizedGrouping } from "@notesnook/core";
import { useThemeColors } from "@notesnook/theme";
import Clipboard from "@react-native-clipboard/clipboard";
import React, { useCallback, useEffect, useState } from "react";
import React, { RefObject, useEffect, useState } from "react";
import { View } from "react-native";
import { ActionSheetRef } from "react-native-actions-sheet";
import { ScrollView } from "react-native-gesture-handler";
import { db } from "../../common/database";
import filesystem from "../../common/filesystem";
@@ -27,14 +31,17 @@ import downloadAttachment from "../../common/filesystem/download-attachment";
import { useAttachmentProgress } from "../../hooks/use-attachment-progress";
import picker from "../../screens/editor/tiptap/picker";
import {
ToastManager,
eSendEvent,
presentSheet,
ToastEvent
presentSheet
} from "../../services/event-manager";
import PremiumService from "../../services/premium";
import { useAttachmentStore } from "../../stores/use-attachment-store";
import { useThemeStore } from "../../stores/use-theme-store";
import { eCloseAttachmentDialog, eCloseSheet } from "../../utils/events";
import {
eCloseAttachmentDialog,
eCloseSheet,
eDBItemUpdate
} from "../../utils/events";
import { SIZE } from "../../utils/size";
import { sleep } from "../../utils/time";
import { Dialog } from "../dialog";
@@ -46,28 +53,37 @@ import { Notice } from "../ui/notice";
import { PressableButton } from "../ui/pressable";
import Heading from "../ui/typography/heading";
import Paragraph from "../ui/typography/paragraph";
import { formatBytes } from "@notesnook/common";
const Actions = ({ attachment, setAttachments, fwdRef }) => {
const colors = useThemeStore((state) => state.colors);
const contextId = attachment.metadata.hash;
const [filename, setFilename] = useState(attachment.metadata.filename);
const Actions = ({
attachment,
close,
setAttachments,
fwdRef
}: {
attachment: Attachment;
setAttachments: (attachments?: VirtualizedGrouping<Attachment>) => void;
close: () => void;
fwdRef: RefObject<ActionSheetRef>;
}) => {
const { colors } = useThemeColors();
const contextId = attachment.hash;
const [filename, setFilename] = useState(attachment.filename);
const [currentProgress] = useAttachmentProgress(attachment);
const [failed, setFailed] = useState(attachment.failed);
const [notes, setNotes] = useState([]);
const [loading, setLoading] = useState({
name: null
});
const [failed, setFailed] = useState<string | undefined>(attachment.failed);
const [notes, setNotes] = useState<Note[]>([]);
const [loading, setLoading] = useState<{
name?: string;
}>({});
const actions = [
{
name: "Download",
onPress: async () => {
if (currentProgress) {
await db.fs.cancel(attachment.metadata.hash, "download");
useAttachmentStore.getState().remove(attachment.metadata.hash);
await db.fs().cancel(attachment.hash);
useAttachmentStore.getState().remove(attachment.hash);
}
downloadAttachment(attachment.metadata.hash, false);
downloadAttachment(attachment.hash, false);
eSendEvent(eCloseSheet, contextId);
},
icon: "download"
@@ -76,7 +92,7 @@ const Actions = ({ attachment, setAttachments, fwdRef }) => {
name: "Reupload",
onPress: async () => {
if (!PremiumService.get()) {
ToastEvent.show({
ToastManager.show({
heading: "Upgrade to pro",
type: "error",
context: "local"
@@ -85,9 +101,9 @@ const Actions = ({ attachment, setAttachments, fwdRef }) => {
}
await picker.pick({
reupload: true,
hash: attachment.metadata.hash,
hash: attachment.hash,
context: contextId,
type: attachment.metadata.type
type: attachment.type
});
},
icon: "upload"
@@ -98,28 +114,29 @@ const Actions = ({ attachment, setAttachments, fwdRef }) => {
setLoading({
name: "Run file check"
});
let res = await filesystem.checkAttachment(attachment.metadata.hash);
let res = await filesystem.checkAttachment(attachment.hash);
if (res.failed) {
db.attachments.markAsFailed(attachment.id, res.failed);
setFailed(res.failed);
ToastEvent.show({
ToastManager.show({
heading: "File check failed with error: " + res.failed,
type: "error",
context: "local"
});
} else {
setFailed(null);
db.attachments.markAsFailed(attachment.id, null);
ToastEvent.show({
setFailed(undefined);
db.attachments.markAsFailed(attachment.id);
eSendEvent(eDBItemUpdate, attachment.id);
ToastManager.show({
heading: "File check passed",
type: "success",
context: "local"
});
}
setAttachments([...db.attachments.all]);
setAttachments();
setLoading({
name: null
name: undefined
});
},
icon: "file-check"
@@ -128,19 +145,20 @@ const Actions = ({ attachment, setAttachments, fwdRef }) => {
name: "Rename",
onPress: () => {
presentDialog({
context: contextId,
context: contextId as any,
input: true,
title: "Rename file",
paragraph: "Enter a new name for the file",
defaultValue: attachment.metadata.filename,
defaultValue: attachment.filename,
positivePress: async (value) => {
if (value && value.length > 0) {
await db.attachments.add({
hash: attachment.metadata.hash,
hash: attachment.hash,
filename: value
});
setFilename(value);
setAttachments([...db.attachments.all]);
setAttachments();
eSendEvent(eDBItemUpdate, attachment.id);
}
},
positiveText: "Rename"
@@ -151,34 +169,23 @@ const Actions = ({ attachment, setAttachments, fwdRef }) => {
{
name: "Delete",
onPress: async () => {
await db.attachments.remove(attachment.metadata.hash, false);
setAttachments([...db.attachments.all]);
eSendEvent(eCloseSheet, contextId);
await db.attachments.remove(attachment.hash, false);
setAttachments();
eSendEvent(eDBItemUpdate, attachment.id);
close();
},
icon: "delete-outline"
}
];
const getNotes = useCallback(() => {
let allNotes = db.notes.all;
let attachmentNotes = attachment.noteIds?.map((id) => {
let index = allNotes?.findIndex((note) => id === note.id);
if (index !== -1) {
return allNotes[index];
} else {
return {
type: "notfound",
title: `Note with id ${id} does not exist.`,
id: id
};
}
});
return attachmentNotes;
}, [attachment.noteIds]);
useEffect(() => {
setNotes(getNotes());
}, [attachment, getNotes]);
db.relations
.to(attachment, "note")
.selector.items()
.then((items) => {
setNotes(items);
});
}, [attachment]);
return (
<ScrollView
@@ -194,7 +201,7 @@ const Actions = ({ attachment, setAttachments, fwdRef }) => {
<View
style={{
borderBottomWidth: 1,
borderBottomColor: colors.nav,
borderBottomColor: colors.secondary.background,
marginBottom: notes && notes.length > 0 ? 0 : 12
}}
>
@@ -219,45 +226,45 @@ const Actions = ({ attachment, setAttachments, fwdRef }) => {
style={{
marginRight: 10
}}
color={colors.icon}
color={colors.secondary.paragraph}
>
{attachment.metadata.type}
{attachment.type}
</Paragraph>
<Paragraph
style={{
marginRight: 10
}}
size={SIZE.xs}
color={colors.icon}
color={colors.secondary.paragraph}
>
{formatBytes(attachment.length)}
{formatBytes(attachment.size)}
</Paragraph>
{attachment.noteIds ? (
{notes.length ? (
<Paragraph
style={{
marginRight: 10
}}
size={SIZE.xs}
color={colors.icon}
color={colors.secondary.paragraph}
>
{attachment.noteIds.length} note
{attachment.noteIds.length > 1 ? "s" : ""}
{notes.length} note
{notes.length > 1 ? "s" : ""}
</Paragraph>
) : null}
<Paragraph
onPress={() => {
Clipboard.setString(attachment.metadata.hash);
ToastEvent.show({
Clipboard.setString(attachment.hash);
ToastManager.show({
type: "success",
heading: "Attachment hash copied",
context: "local"
});
}}
size={SIZE.xs}
color={colors.icon}
color={colors.secondary.paragraph}
>
{attachment.metadata.hash}
{attachment.hash}
</Paragraph>
</View>
@@ -268,7 +275,7 @@ const Actions = ({ attachment, setAttachments, fwdRef }) => {
<View
style={{
borderBottomWidth: 1,
borderBottomColor: colors.nav,
borderBottomColor: colors.secondary.background,
marginBottom: 12,
paddingVertical: 12
}}
@@ -286,21 +293,11 @@ const Actions = ({ attachment, setAttachments, fwdRef }) => {
{notes.map((item) => (
<PressableButton
onPress={async () => {
if (item.type === "notfound") {
ToastEvent.show({
heading: "Note not found",
message:
"A note with the given id was not found. Maybe you have deleted the note or moved it to trash already.",
type: "error",
context: "local"
});
return;
}
eSendEvent(eCloseSheet, contextId);
await sleep(150);
eSendEvent(eCloseAttachmentDialog);
await sleep(300);
openNote(item, item.type === "trash");
openNote(item, (item as any).type === "trash");
}}
customStyle={{
paddingVertical: 12,
@@ -321,17 +318,16 @@ const Actions = ({ attachment, setAttachments, fwdRef }) => {
<Button
key={item.name}
buttonType={{
text: item.on
? colors.accent
: item.name === "Delete" || item.name === "PermDelete"
? colors.errorText
: colors.pri
text:
item.name === "Delete" || item.name === "PermDelete"
? colors.error.paragraph
: colors.primary.paragraph
}}
onPress={item.onPress}
title={item.name}
icon={item.icon}
loading={loading?.name === item.name}
type={item.on ? "shade" : "gray"}
type="gray"
fontSize={SIZE.sm}
style={{
borderRadius: 0,
@@ -359,11 +355,20 @@ const Actions = ({ attachment, setAttachments, fwdRef }) => {
);
};
Actions.present = (attachment, set, context) => {
Actions.present = (
attachment: Attachment,
set: (attachments?: VirtualizedGrouping<Attachment>) => void,
context?: string
) => {
presentSheet({
context: context,
component: (ref) => (
<Actions fwdRef={ref} setAttachments={set} attachment={attachment} />
component: (ref, close) => (
<Actions
fwdRef={ref}
setAttachments={set}
close={close}
attachment={attachment}
/>
)
});
};

View File

@@ -1,172 +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 { formatBytes } from "@notesnook/common";
import React 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 { useThemeStore } from "../../stores/use-theme-store";
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";
function getFileExtension(filename) {
var ext = /^.+\.([^.]+)$/.exec(filename);
return ext == null ? "" : ext[1];
}
export const AttachmentItem = ({
attachment,
encryption,
setAttachments,
pressable = true,
hideWhenNotDownloading,
context
}) => {
const colors = useThemeStore((state) => state.colors);
const [currentProgress, setCurrentProgress] = useAttachmentProgress(
attachment,
encryption
);
const onPress = () => {
if (!pressable) return;
Actions.present(attachment, setAttachments, context);
};
return hideWhenNotDownloading &&
(!currentProgress || !currentProgress.value) ? null : (
<TouchableOpacity
activeOpacity={0.9}
onPress={onPress}
style={{
flexDirection: "row",
marginVertical: 5,
justifyContent: "space-between",
padding: 12,
paddingVertical: 6,
borderRadius: 5,
backgroundColor: colors.nav
}}
type="grayBg"
>
<View
style={{
flexShrink: 1,
flexDirection: "row",
alignItems: "center"
}}
>
<View
style={{
justifyContent: "center",
alignItems: "center",
marginLeft: -5
}}
>
<Icon name="file" size={SIZE.xxxl} color={colors.icon} />
<Paragraph
adjustsFontSizeToFit
size={6}
color={colors.light}
style={{
position: "absolute"
}}
>
{getFileExtension(attachment.metadata.filename).toUpperCase()}
</Paragraph>
</View>
<View
style={{
flexShrink: 1,
marginLeft: 10
}}
>
<Paragraph
size={SIZE.sm - 1}
style={{
flexWrap: "wrap",
marginBottom: 2.5
}}
numberOfLines={1}
lineBreakMode="middle"
color={colors.pri}
>
{attachment.metadata.filename}
</Paragraph>
{!hideWhenNotDownloading ? (
<Paragraph color={colors.icon} size={SIZE.xs}>
{formatBytes(attachment.length)}{" "}
{currentProgress?.type
? "(" + currentProgress.type + "ing - tap to cancel)"
: ""}
</Paragraph>
) : null}
</View>
</View>
{currentProgress ? (
<TouchableOpacity
activeOpacity={0.9}
onPress={() => {
if (encryption || !pressable) return;
db.fs.cancel(attachment.metadata.hash);
setCurrentProgress(null);
}}
style={{
justifyContent: "center",
marginLeft: 5,
marginTop: 5,
marginRight: -5
}}
>
<ProgressCircleComponent
size={SIZE.xxl}
progress={currentProgress?.value ? currentProgress?.value / 100 : 0}
showsText
textStyle={{
fontSize: 10
}}
color={colors.accent}
formatText={(progress) => (progress * 100).toFixed(0)}
borderWidth={0}
thickness={2}
/>
</TouchableOpacity>
) : (
<>
{attachment.failed ? (
<IconButton
onPress={onPress}
name="alert-circle-outline"
color={colors.errorText}
/>
) : null}
</>
)}
</TouchableOpacity>
);
};

View File

@@ -0,0 +1,191 @@
/*
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 { formatBytes } from "@notesnook/common";
import { Attachment, VirtualizedGrouping } from "@notesnook/core";
import { useThemeColors } from "@notesnook/theme";
import React 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 { 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";
function getFileExtension(filename: string) {
const ext = /^.+\.([^.]+)$/.exec(filename);
return ext == null ? "" : ext[1];
}
export const AttachmentItem = ({
id,
attachments,
encryption,
setAttachments,
pressable = true,
hideWhenNotDownloading,
context
}: {
id: string | number;
attachments?: VirtualizedGrouping<Attachment>;
encryption?: boolean;
setAttachments: (attachments: any) => void;
pressable?: boolean;
hideWhenNotDownloading?: boolean;
context?: string;
}) => {
const [attachment] = useDBItem(id, "attachment", attachments);
const { colors } = useThemeColors();
const [currentProgress, setCurrentProgress] = useAttachmentProgress(
attachment,
encryption
);
const onPress = () => {
if (!pressable || !attachment) return;
Actions.present(attachment, setAttachments, context);
};
return hideWhenNotDownloading &&
(!currentProgress || !currentProgress.value) ? null : (
<TouchableOpacity
activeOpacity={0.9}
onPress={onPress}
style={{
flexDirection: "row",
marginVertical: 5,
justifyContent: "space-between",
padding: 12,
paddingVertical: 6,
borderRadius: 5,
backgroundColor: colors.secondary.background,
minHeight: 45
}}
>
{!attachment ? null : (
<>
<View
style={{
flexShrink: 1,
flexDirection: "row",
alignItems: "center"
}}
>
<View
style={{
justifyContent: "center",
alignItems: "center",
marginLeft: -5
}}
>
<Icon name="file" size={SIZE.xxxl} color={colors.primary.icon} />
<Paragraph
adjustsFontSizeToFit
size={6}
color={colors.static.white}
style={{
position: "absolute"
}}
>
{getFileExtension(attachment.filename).toUpperCase()}
</Paragraph>
</View>
<View
style={{
flexShrink: 1,
marginLeft: 10
}}
>
<Paragraph
size={SIZE.sm - 1}
style={{
flexWrap: "wrap",
marginBottom: 2.5
}}
numberOfLines={1}
lineBreakMode="middle"
color={colors.primary.paragraph}
>
{attachment.filename}
</Paragraph>
{!hideWhenNotDownloading ? (
<Paragraph color={colors.secondary.paragraph} size={SIZE.xs}>
{formatBytes(attachment.size)}{" "}
{currentProgress?.type
? "(" + currentProgress.type + "ing - tap to cancel)"
: ""}
</Paragraph>
) : null}
</View>
</View>
{currentProgress ? (
<TouchableOpacity
activeOpacity={0.9}
onPress={() => {
if (encryption || !pressable) return;
db.fs().cancel(attachment.hash);
setCurrentProgress(undefined);
}}
style={{
justifyContent: "center",
marginLeft: 5,
marginTop: 5,
marginRight: -5
}}
>
<ProgressCircleComponent
size={SIZE.xxl}
progress={
currentProgress?.value ? currentProgress?.value / 100 : 0
}
showsText
textStyle={{
fontSize: 10
}}
color={colors.primary.accent}
formatText={(progress) => (progress * 100).toFixed(0)}
borderWidth={0}
thickness={2}
/>
</TouchableOpacity>
) : (
<>
{attachment.failed ? (
<IconButton
onPress={onPress}
name="alert-circle-outline"
color={colors.error.paragraph}
/>
) : null}
</>
)}
</>
)}
</TouchableOpacity>
);
};

View File

@@ -21,17 +21,31 @@ import React, { useRef, useState } from "react";
import { Platform, View } from "react-native";
import { db } from "../../common/database";
import { downloadAttachments } from "../../common/filesystem/download-attachment";
import { presentSheet } from "../../services/event-manager";
import {
PresentSheetOptions,
presentSheet
} from "../../services/event-manager";
import { Button } from "../ui/button";
import Heading from "../ui/typography/heading";
import Paragraph from "../ui/typography/paragraph";
import { ProgressBarComponent } from "../ui/svg/lazy";
import { useThemeStore } from "../../stores/use-theme-store";
import { useThemeColors } from "@notesnook/theme";
import { FlatList } from "react-native-actions-sheet";
import { AttachmentItem } from "./attachment-item";
import { Attachment, VirtualizedGrouping } from "@notesnook/core";
const DownloadAttachments = ({ close, attachments, isNote, update }) => {
const colors = useThemeStore((state) => state.colors);
const DownloadAttachments = ({
close,
attachments,
isNote,
update
}: {
attachments: VirtualizedGrouping<Attachment>;
close?: ((ctx?: string | undefined) => void) | undefined;
isNote?: boolean;
update?: (props: PresentSheetOptions) => void;
}) => {
const { colors } = useThemeColors();
const [downloading, setDownloading] = useState(false);
const [progress, setProgress] = useState({
value: 0,
@@ -39,63 +53,57 @@ const DownloadAttachments = ({ close, attachments, isNote, update }) => {
});
const [result, setResult] = useState(new Map());
const canceled = useRef(false);
const groupId = useRef();
const groupId = useRef<string>();
const onDownload = async () => {
update({
update?.({
disableClosing: true
});
} as PresentSheetOptions);
setDownloading(true);
canceled.current = false;
groupId.current = Date.now().toString();
const result = await downloadAttachments(
attachments,
(progress, statusText) => setProgress({ value: progress, statusText }),
await attachments.ids(),
(progress: number, statusText: string) =>
setProgress({ value: progress, statusText }),
canceled,
groupId.current
);
if (canceled.current) return;
setResult(result || new Map());
setDownloading(false);
update({
update?.({
disableClosing: false
});
} as PresentSheetOptions);
};
const cancel = async () => {
update({
update?.({
disableClosing: false
});
} as PresentSheetOptions);
canceled.current = true;
if (!groupId.current) return;
console.log(groupId.current, "canceling groupId downloads");
await db.fs.cancel(groupId.current);
await db.fs().cancel(groupId.current, "download");
setDownloading(false);
groupId.current = null;
};
const successResults = () => {
const results = [];
for (let [key, value] of result.entries()) {
if (value.status === 1) results.push(db.attachments.attachment(key));
}
return results;
groupId.current = undefined;
};
const failedResults = () => {
const results = [];
for (let [key, value] of result.entries()) {
if (value.status === 0) results.push(db.attachments.attachment(key));
for (const value of result.values()) {
if (value.status === 0) results.push(value.attachment);
}
return results;
};
function getResultText() {
const downloadedAttachmentsCount =
attachments.length - failedResults().length;
attachments?.placeholders?.length - failedResults().length;
if (downloadedAttachmentsCount === 0)
return "Failed to download all attachments";
return `Successfully downloaded ${downloadedAttachmentsCount}/${
attachments.length
attachments?.placeholders.length
} attachments as a zip file at ${
Platform.OS === "android" ? "the selected folder" : "Notesnook/downloads"
}`;
@@ -157,9 +165,13 @@ const DownloadAttachments = ({ close, attachments, isNote, update }) => {
width={null}
animated={true}
useNativeDriver
progress={progress.value ? progress.value / attachments.length : 0}
unfilledColor={colors.nav}
color={colors.accent}
progress={
progress.value
? progress.value / attachments.placeholders?.length
: 0
}
unfilledColor={colors.secondary.background}
color={colors.primary.accent}
borderWidth={0}
/>
</View>
@@ -170,11 +182,11 @@ const DownloadAttachments = ({ close, attachments, isNote, update }) => {
maxHeight: 300,
width: "100%",
minHeight: 60,
backgroundColor: colors.nav,
backgroundColor: colors.secondary.background,
borderRadius: 5,
marginVertical: 12
}}
data={downloading ? attachments : []}
data={downloading ? attachments.placeholders : undefined}
ListEmptyComponent={
<View
style={{
@@ -184,17 +196,20 @@ const DownloadAttachments = ({ close, attachments, isNote, update }) => {
height: 60
}}
>
<Paragraph color={colors.icon}>No downloads in progress.</Paragraph>
<Paragraph color={colors.secondary.paragraph}>
No downloads in progress.
</Paragraph>
</View>
}
keyExtractor={(item) => item.id}
renderItem={({ item }) => {
keyExtractor={(index) => "attachment" + index}
renderItem={({ index }) => {
return (
<AttachmentItem
attachment={item}
id={index}
setAttachments={() => {}}
pressable={false}
hideWhenNotDownloading={true}
attachments={attachments}
/>
);
}}
@@ -207,7 +222,9 @@ const DownloadAttachments = ({ close, attachments, isNote, update }) => {
borderRadius: 100,
marginTop: 20
}}
onPress={close}
onPress={() => {
close?.();
}}
type="accent"
title="Done"
/>
@@ -225,7 +242,9 @@ const DownloadAttachments = ({ close, attachments, isNote, update }) => {
borderRadius: 100,
marginRight: 5
}}
onPress={close}
onPress={() => {
close?.();
}}
type="grayBg"
title="No"
/>
@@ -256,7 +275,11 @@ const DownloadAttachments = ({ close, attachments, isNote, update }) => {
);
};
DownloadAttachments.present = (context, attachments, isNote) => {
DownloadAttachments.present = (
context: string,
attachments: VirtualizedGrouping<Attachment>,
isNote?: boolean
) => {
presentSheet({
context: context,
component: (ref, close, update) => (

View File

@@ -17,16 +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 React, { useRef, useState } from "react";
import {
Attachment,
Note,
SortOptions,
VirtualizedGrouping
} from "@notesnook/core";
import { FilteredSelector } from "@notesnook/core/dist/database/sql-collection";
import { useThemeColors } from "@notesnook/theme";
import React, { useEffect, useRef, useState } from "react";
import { ActivityIndicator, ScrollView, View } from "react-native";
import { FlashList } from "react-native-actions-sheet/dist/src/views/FlashList";
import Icon from "react-native-vector-icons/MaterialCommunityIcons";
import { db } from "../../common/database";
import filesystem from "../../common/filesystem";
import { presentSheet } from "../../services/event-manager";
import { useThemeStore } from "../../stores/use-theme-store";
import { useSettingStore } from "../../stores/use-setting-store";
import { SIZE } from "../../utils/size";
import SheetProvider from "../sheet-provider";
import { Button } from "../ui/button";
import { IconButton } from "../ui/icon-button";
import Input from "../ui/input";
import Seperator from "../ui/seperator";
@@ -34,79 +43,81 @@ import Heading from "../ui/typography/heading";
import Paragraph from "../ui/typography/paragraph";
import { AttachmentItem } from "./attachment-item";
import DownloadAttachments from "./download-attachments";
import { Button } from "../ui/button";
import {
isAudio,
isDocument,
isImage,
isVideo
} from "@notesnook/core/utils/filename";
import { useSettingStore } from "../../stores/use-setting-store";
import { FlashList } from "react-native-actions-sheet/dist/src/views/FlashList";
export const AttachmentDialog = ({ note }) => {
const colors = useThemeStore((state) => state.colors);
const DEFAULT_SORTING: SortOptions = {
sortBy: "dateEdited",
sortDirection: "desc"
};
export const AttachmentDialog = ({ note }: { note?: Note }) => {
const { colors } = useThemeColors();
const { height } = useSettingStore((state) => state.dimensions);
const [attachments, setAttachments] = useState(
note
? db.attachments.ofNote(note.id, "all")
: [...(db.attachments.all || [])]
);
const attachmentSearchValue = useRef();
const searchTimer = useRef();
const [attachments, setAttachments] =
useState<VirtualizedGrouping<Attachment>>();
const attachmentSearchValue = useRef<string>();
const searchTimer = useRef<NodeJS.Timeout>();
const [loading, setLoading] = useState(false);
const [currentFilter, setCurrentFilter] = useState("all");
const onChangeText = (text) => {
const attachments = note
? db.attachments.ofNote(note.id, "all")
: [...(db.attachments.all || [])];
const refresh = React.useCallback(() => {
if (note) {
db.attachments.ofNote(note.id, "all").sorted(DEFAULT_SORTING);
} else {
db.attachments.all
.sorted(DEFAULT_SORTING)
.then((attachments) => setAttachments(attachments));
}
}, [note]);
useEffect(() => {
refresh();
}, [note, refresh]);
const onChangeText = async (text: string) => {
attachmentSearchValue.current = text;
if (
!attachmentSearchValue.current ||
attachmentSearchValue.current === ""
) {
setAttachments([...attachments]);
setAttachments(await filterAttachments(currentFilter));
refresh();
}
clearTimeout(searchTimer.current);
searchTimer.current = setTimeout(() => {
let results = db.lookup.attachments(
attachments,
attachmentSearchValue.current
);
searchTimer.current = setTimeout(async () => {
const results = await db.lookup
.attachments(attachmentSearchValue.current as string)
.sorted();
if (results.length === 0) return;
setAttachments(results);
}, 300);
};
const renderItem = ({ item }) => (
const renderItem = ({ index }: { item: boolean; index: number }) => (
<AttachmentItem
setAttachments={setAttachments}
attachment={item}
setAttachments={async () => {
setAttachments(await filterAttachments(currentFilter));
}}
attachments={attachments}
id={index}
context="attachments-list"
/>
);
const onCheck = async () => {
if (!attachments) return;
setLoading(true);
const checkedAttachments = [];
for (let attachment of attachments) {
let result = await filesystem.checkAttachment(attachment.metadata.hash);
for (let i = 0; i < attachments.placeholders.length; i++) {
const attachment = (await attachments.item(i))?.item;
if (!attachment) continue;
const result = await filesystem.checkAttachment(attachment.hash);
if (result.failed) {
await db.attachments.markAsFailed(
attachment.metadata.hash,
result.failed
);
await db.attachments.markAsFailed(attachment.hash, result.failed);
} else {
await db.attachments.markAsFailed(attachment.id, null);
await db.attachments.markAsFailed(attachment.id, undefined);
}
checkedAttachments.push(
db.attachments.attachment(attachment.metadata.hash)
);
setAttachments([...checkedAttachments]);
}
refresh();
setLoading(false);
};
@@ -133,31 +144,33 @@ export const AttachmentDialog = ({ note }) => {
}
];
const filterAttachments = (type) => {
const attachments = note
? db.attachments.ofNote(note.id, "all")
: [...(db.attachments.all || [])];
isDocument;
const filterAttachments = async (type: string) => {
let items: FilteredSelector<Attachment> = db.attachments.all;
switch (type) {
case "all":
return attachments;
items = db.attachments.all;
break;
case "images":
return attachments.filter((attachment) =>
isImage(attachment.metadata.type)
);
items = note
? db.attachments.ofNote(note.id, "images")
: db.attachments.images;
break;
case "video":
return attachments.filter((attachment) =>
isVideo(attachment.metadata.type)
);
items = items = note
? db.attachments.ofNote(note.id, "all")
: db.attachments.videos;
break;
case "audio":
return attachments.filter((attachment) =>
isAudio(attachment.metadata.type)
);
items = db.attachments.all;
break;
case "documents":
return attachments.filter((attachment) =>
isDocument(attachment.metadata.type)
);
items = note
? db.attachments.ofNote(note.id, "all")
: db.attachments.documents;
}
return await items.sorted(DEFAULT_SORTING);
};
return (
@@ -201,7 +214,7 @@ export const AttachmentDialog = ({ note }) => {
width: 40,
marginRight: 10
}}
color={colors.pri}
color={colors.primary.paragraph}
size={SIZE.lg}
onPress={onCheck}
/>
@@ -213,8 +226,9 @@ export const AttachmentDialog = ({ note }) => {
height: 40,
width: 40
}}
color={colors.pri}
color={colors.primary.paragraph}
onPress={() => {
if (!attachments) return;
DownloadAttachments.present(
"attachments-list",
attachments,
@@ -231,7 +245,7 @@ export const AttachmentDialog = ({ note }) => {
placeholder="Filter attachments by filename, type or hash"
onChangeText={onChangeText}
onSubmit={() => {
onChangeText(attachmentSearchValue.current);
onChangeText(attachmentSearchValue.current as string);
}}
/>
@@ -241,7 +255,7 @@ export const AttachmentDialog = ({ note }) => {
width: "100%",
height: 50,
flexDirection: "row",
backgroundColor: colors.bg
backgroundColor: colors.primary.background
}}
contentContainerStyle={{
minWidth: "100%",
@@ -253,10 +267,7 @@ export const AttachmentDialog = ({ note }) => {
<Button
type={currentFilter === item.filterBy ? "grayAccent" : "gray"}
key={item.title}
title={
item.title +
` (${filterAttachments(item.filterBy)?.length || 0})`
}
title={item.title}
style={{
borderRadius: 0,
borderBottomWidth: 1,
@@ -264,11 +275,11 @@ export const AttachmentDialog = ({ note }) => {
borderBottomColor:
currentFilter !== item.filterBy
? "transparent"
: colors.accent
: colors.primary.accent
}}
onPress={() => {
onPress={async () => {
setCurrentFilter(item.filterBy);
setAttachments(filterAttachments(item.filterBy));
setAttachments(await filterAttachments(item.filterBy));
}}
/>
))}
@@ -285,7 +296,7 @@ export const AttachmentDialog = ({ note }) => {
alignItems: "center"
}}
>
<Icon name="attachment" size={60} color={colors.icon} />
<Icon name="attachment" size={60} color={colors.secondary.icon} />
<Paragraph>
{note ? "No attachments on this note" : "No attachments"}
</Paragraph>
@@ -299,26 +310,30 @@ export const AttachmentDialog = ({ note }) => {
/>
}
estimatedItemSize={50}
data={attachments}
data={attachments?.placeholders}
renderItem={renderItem}
/>
<Paragraph
color={colors.icon}
color={colors.secondary.paragraph}
size={SIZE.xs}
style={{
textAlign: "center",
marginTop: 10
}}
>
<Icon name="shield-key-outline" size={SIZE.xs} color={colors.icon} />
<Icon
name="shield-key-outline"
size={SIZE.xs}
color={colors.primary.icon}
/>
{" "}All attachments are end-to-end encrypted.
</Paragraph>
</View>
);
};
AttachmentDialog.present = (note) => {
AttachmentDialog.present = (note?: Note) => {
presentSheet({
component: () => <AttachmentDialog note={note} />
});

View File

@@ -17,14 +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 { 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 { useThemeStore } from "../../stores/use-theme-store";
import { useUserStore } from "../../stores/use-user-store";
import { eCloseLoginDialog, eOpenLoginDialog } from "../../utils/events";
import { sleep } from "../../utils/time";
import BaseDialog from "../dialog/base-dialog";
@@ -34,7 +36,6 @@ 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,
@@ -44,7 +45,7 @@ export const AuthMode = {
};
const AuthModal = () => {
const colors = useThemeStore((state) => state.colors);
const { colors } = useThemeColors();
const [visible, setVisible] = useState(false);
const [currentAuthMode, setCurrentAuthMode] = useState(AuthMode.login);
const actionSheetRef = useRef();
@@ -68,6 +69,9 @@ const AuthModal = () => {
}
const close = () => {
useUserStore.setState({
disableAppLockRequests: false
});
actionSheetRef.current?.hide();
setCurrentAuthMode(AuthMode.login);
setVisible(false);
@@ -77,19 +81,27 @@ const AuthModal = () => {
<BaseDialog
overlayOpacity={0}
statusBarTranslucent={false}
onShow={() => {
useUserStore.setState({
disableAppLockRequests: true
});
}}
onRequestClose={currentAuthMode !== AuthMode.welcomeSignup && close}
visible={true}
onClose={close}
useSafeArea={false}
bounce={false}
background={colors.bg}
background={colors.primary.background}
transparent={false}
animated={false}
centered={false}
enableSheetKeyboardHandler
>
<KeyboardAwareScrollView
style={{
width: "100%"
}}
enableAutomaticScroll={false}
keyboardShouldPersistTaps="handled"
>
{currentAuthMode !== AuthMode.login ? (
@@ -112,7 +124,7 @@ const AuthModal = () => {
paddingTop: Platform.OS === "android" ? 0 : insets.top,
top: 0,
zIndex: 999,
backgroundColor: colors.nav,
backgroundColor: colors.secondary.background,
width: "100%"
}}
>
@@ -135,7 +147,7 @@ const AuthModal = () => {
onPress={() => {
hideAuth();
}}
color={colors.pri}
color={colors.primary.paragraph}
/>
)}

View File

@@ -23,7 +23,7 @@ import { db } from "../../common/database";
import {
eSendEvent,
presentSheet,
ToastEvent
ToastManager
} from "../../services/event-manager";
import { useUserStore } from "../../stores/use-user-store";
import { eCloseSheet } from "../../utils/events";
@@ -45,7 +45,7 @@ export const ChangePassword = () => {
const changePassword = async () => {
if (!user?.isEmailConfirmed) {
ToastEvent.show({
ToastManager.show({
heading: "Email not confirmed",
message: "Please confirm your email to change account password",
type: "error",
@@ -54,7 +54,7 @@ export const ChangePassword = () => {
return;
}
if (error || !oldPassword.current || !password.current) {
ToastEvent.show({
ToastManager.show({
heading: "All fields required",
message: "Fill all the fields and try again.",
type: "error",
@@ -66,7 +66,7 @@ export const ChangePassword = () => {
try {
await db.user.clearSessions();
await db.user.changePassword(oldPassword.current, password.current);
ToastEvent.show({
ToastManager.show({
heading: "Account password updated",
type: "success",
context: "global"
@@ -75,7 +75,7 @@ export const ChangePassword = () => {
eSendEvent(eCloseSheet);
} catch (e) {
setLoading(false);
ToastEvent.show({
ToastManager.show({
heading: "Failed to change password",
message: e.message,
type: "error",

View File

@@ -22,9 +22,9 @@ import { View } from "react-native";
import ActionSheet from "react-native-actions-sheet";
import { db } from "../../common/database";
import { DDS } from "../../services/device-detection";
import { ToastEvent } from "../../services/event-manager";
import { ToastManager } from "../../services/event-manager";
import SettingsService from "../../services/settings";
import { useThemeStore } from "../../stores/use-theme-store";
import { useThemeColors } from "@notesnook/theme";
import DialogHeader from "../dialog/dialog-header";
import { Button } from "../ui/button";
import { IconButton } from "../ui/icon-button";
@@ -34,7 +34,7 @@ import Heading from "../ui/typography/heading";
import Paragraph from "../ui/typography/paragraph";
export const ForgotPassword = () => {
const colors = useThemeStore((state) => state.colors);
const { colors } = useThemeColors("sheet");
const email = useRef();
const emailInputRef = useRef();
const [error, setError] = useState(false);
@@ -43,7 +43,7 @@ export const ForgotPassword = () => {
const sendRecoveryEmail = async () => {
if (!email.current || error) {
ToastEvent.show({
ToastManager.show({
heading: "Account email is required.",
type: "error",
context: "local"
@@ -63,7 +63,7 @@ export const ForgotPassword = () => {
SettingsService.set({
lastRecoveryEmailTime: Date.now()
});
ToastEvent.show({
ToastManager.show({
heading: "Check your email to reset password",
message: `Recovery email has been sent to ${email.current.toLowerCase()}`,
type: "success",
@@ -74,7 +74,7 @@ export const ForgotPassword = () => {
setSent(true);
} catch (e) {
setLoading(false);
ToastEvent.show({
ToastManager.show({
heading: "Recovery email not sent",
message: e.message,
type: "error",
@@ -117,7 +117,7 @@ export const ForgotPassword = () => {
width: null,
height: null
}}
color={colors.accent}
color={colors.primary.accent}
name="email"
size={50}
/>
@@ -134,7 +134,7 @@ export const ForgotPassword = () => {
<View
style={{
borderRadius: DDS.isTab ? 5 : 0,
backgroundColor: colors.bg,
backgroundColor: colors.primary.background,
zIndex: 10,
width: "100%",
padding: 12

View File

@@ -65,7 +65,7 @@ const Auth = ({ navigation, route }) => {
onPress={() => {
hideAuth();
}}
color={colors.pri}
color={colors.primary.paragraph}
customStyle={{
position: 'absolute',
zIndex: 999,

View File

@@ -22,7 +22,7 @@ import { TouchableOpacity, View, useWindowDimensions } from "react-native";
import { SheetManager } from "react-native-actions-sheet";
import { DDS } from "../../services/device-detection";
import { eSendEvent } from "../../services/event-manager";
import { useThemeStore } from "../../stores/use-theme-store";
import { useThemeColors } from "@notesnook/theme";
import { SIZE } from "../../utils/size";
import { sleep } from "../../utils/time";
import SheetProvider from "../sheet-provider";
@@ -46,7 +46,7 @@ const LoginSteps = {
};
export const Login = ({ changeMode }) => {
const colors = useThemeStore((state) => state.colors);
const { colors } = useThemeColors();
const [focused, setFocused] = useState(false);
const {
step,
@@ -92,7 +92,7 @@ export const Login = ({ changeMode }) => {
<View
style={{
borderRadius: DDS.isTab ? 5 : 0,
backgroundColor: colors.bg,
backgroundColor: colors.primary.background,
zIndex: 10,
width: "100%",
alignSelf: "center"
@@ -102,13 +102,13 @@ export const Login = ({ changeMode }) => {
style={{
justifyContent: "flex-end",
paddingHorizontal: 20,
backgroundColor: colors.nav,
backgroundColor: colors.secondary.background,
borderBottomWidth: 1,
marginBottom: 12,
borderBottomColor: colors.border,
borderBottomColor: colors.primary.border,
alignSelf: deviceMode !== "mobile" ? "center" : undefined,
borderWidth: deviceMode !== "mobile" ? 1 : null,
borderColor: deviceMode !== "mobile" ? colors.border : null,
borderColor: deviceMode !== "mobile" ? colors.primary.border : null,
borderRadius: deviceMode !== "mobile" ? 20 : null,
marginTop: deviceMode !== "mobile" ? 50 : null,
width: deviceMode === "mobile" ? null : "50%",
@@ -124,7 +124,7 @@ export const Login = ({ changeMode }) => {
style={{
width: 100,
height: 5,
backgroundColor: colors.accent,
backgroundColor: colors.primary.accent,
borderRadius: 2,
marginRight: 7
}}
@@ -134,7 +134,7 @@ export const Login = ({ changeMode }) => {
style={{
width: 20,
height: 5,
backgroundColor: colors.nav,
backgroundColor: colors.secondary.background,
borderRadius: 2
}}
/>
@@ -160,7 +160,7 @@ export const Login = ({ changeMode }) => {
: focused
? "100%"
: "99.9%",
backgroundColor: colors.bg,
backgroundColor: colors.primary.background,
alignSelf: "center",
paddingHorizontal: 20
}}
@@ -281,11 +281,14 @@ export const Login = ({ changeMode }) => {
paddingVertical: 12
}}
>
<Paragraph size={SIZE.xs + 1} color={colors.icon}>
<Paragraph
size={SIZE.xs + 1}
color={colors.secondary.paragraph}
>
Don't have an account?{" "}
<Paragraph
size={SIZE.xs + 1}
style={{ color: colors.accent }}
style={{ color: colors.primary.accent }}
>
Sign up
</Paragraph>

View File

@@ -17,22 +17,27 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { useThemeColors } from "@notesnook/theme";
import React, { useEffect, useState } from "react";
import { Modal, View } from "react-native";
import { View } from "react-native";
import { db } from "../../common/database";
import { MMKV } from "../../common/database/mmkv";
import BiometricService from "../../services/biometrics";
import {
ToastEvent,
ToastManager,
eSendEvent,
eSubscribeEvent
} from "../../services/event-manager";
import { setLoginMessage } from "../../services/message";
import SettingsService from "../../services/settings";
import Sync from "../../services/sync";
import { useThemeStore } from "../../stores/use-theme-store";
import { clearAllStores } from "../../stores";
import { useUserStore } from "../../stores/use-user-store";
import { eLoginSessionExpired, eUserLoggedIn } from "../../utils/events";
import { SIZE } from "../../utils/size";
import { sleep } from "../../utils/time";
import { Dialog } from "../dialog";
import BaseDialog from "../dialog/base-dialog";
import { presentDialog } from "../dialog/functions";
import SheetProvider from "../sheet-provider";
import { Toast } from "../toast";
@@ -56,7 +61,7 @@ function getObfuscatedEmail(email) {
}
export const SessionExpired = () => {
const colors = useThemeStore((state) => state.colors);
const { colors } = useThemeColors();
const [visible, setVisible] = useState(false);
const [focused, setFocused] = useState(false);
const { step, password, email, passwordInputRef, loading, login } = useLogin(
@@ -64,19 +69,23 @@ export const SessionExpired = () => {
eSendEvent(eUserLoggedIn, true);
setVisible(false);
setFocused(false);
}
},
true
);
const logout = async () => {
try {
await db.user.logout();
await BiometricService.resetCredentials();
SettingsService.set({
introCompleted: true
});
setLoginMessage();
SettingsService.resetSettings();
useUserStore.getState().setUser(null);
useUserStore.getState().setSyncing(false);
MMKV.clearStore();
clearAllStores();
setVisible(false);
} catch (e) {
ToastEvent.show({
ToastManager.show({
heading: e.message,
type: "error",
context: "local"
@@ -93,9 +102,9 @@ export const SessionExpired = () => {
const open = React.useCallback(async () => {
try {
let res = await db.user.tokenManager.getToken();
let res = await db.tokenManager.getToken();
if (!res) throw new Error("no token found");
if (db.user.tokenManager._isTokenExpired(res))
if (db.tokenManager._isTokenExpired(res))
throw new Error("token expired");
Sync.run("global", false, true, async (complete) => {
if (!complete) {
@@ -123,22 +132,27 @@ export const SessionExpired = () => {
return (
visible && (
<Modal
<BaseDialog
transparent={false}
background={colors.primary.background}
bounce={false}
animated={false}
centered={false}
onShow={async () => {
await sleep(300);
passwordInputRef.current?.focus();
setFocused(true);
}}
enableSheetKeyboardHandler={true}
visible={true}
>
<SheetProvider context="two_factor_verify" />
<View
style={{
width: focused ? "100%" : "99.9%",
padding: 12,
justifyContent: "center",
flex: 1,
backgroundColor: colors.bg
backgroundColor: colors.primary.background
}}
>
<View
@@ -157,10 +171,10 @@ export const SessionExpired = () => {
height: 60
}}
name="alert"
color={colors.errorText}
color={colors.error.icon}
size={50}
/>
<Heading size={SIZE.xxxl} color={colors.heading}>
<Heading size={SIZE.xxxl} color={colors.primary.heading}>
Session expired
</Heading>
<Paragraph
@@ -224,7 +238,9 @@ export const SessionExpired = () => {
</View>
<Toast context="local" />
<Dialog context="session_expiry" />
</Modal>
<SheetProvider context="two_factor_verify" />
</BaseDialog>
)
);
};

View File

@@ -21,10 +21,10 @@ import React, { useRef, useState } from "react";
import { TouchableOpacity, View, useWindowDimensions } from "react-native";
import { db } from "../../common/database";
import { DDS } from "../../services/device-detection";
import { ToastEvent } from "../../services/event-manager";
import { ToastManager } from "../../services/event-manager";
import { clearMessage, setEmailVerifyMessage } from "../../services/message";
import PremiumService from "../../services/premium";
import { useThemeStore } from "../../stores/use-theme-store";
import { useThemeColors } from "@notesnook/theme";
import { useUserStore } from "../../stores/use-user-store";
import { openLinkInBrowser } from "../../utils/functions";
import { SIZE } from "../../utils/size";
@@ -37,7 +37,7 @@ import { hideAuth } from "./common";
import { useSettingStore } from "../../stores/use-setting-store";
export const Signup = ({ changeMode, trial }) => {
const colors = useThemeStore((state) => state.colors);
const { colors } = useThemeColors();
const email = useRef();
const emailInputRef = useRef();
const passwordInputRef = useRef();
@@ -52,7 +52,7 @@ export const Signup = ({ changeMode, trial }) => {
const { width, height } = useWindowDimensions();
const validateInfo = () => {
if (!password.current || !email.current || !confirmPassword.current) {
ToastEvent.show({
ToastManager.show({
heading: "All fields required",
message: "Fill all the fields and try again",
type: "error",
@@ -67,6 +67,7 @@ export const Signup = ({ changeMode, trial }) => {
const signup = async () => {
if (!validateInfo() || error) return;
if (loading) return;
setLoading(true);
try {
await db.user.signup(email.current.toLowerCase(), password.current);
@@ -84,7 +85,7 @@ export const Signup = ({ changeMode, trial }) => {
}
} catch (e) {
setLoading(false);
ToastEvent.show({
ToastManager.show({
heading: "Signup failed",
message: e.message,
type: "error",
@@ -98,7 +99,7 @@ export const Signup = ({ changeMode, trial }) => {
<View
style={{
borderRadius: DDS.isTab ? 5 : 0,
backgroundColor: colors.bg,
backgroundColor: colors.primary.background,
zIndex: 10,
width: "100%",
alignSelf: "center"
@@ -108,13 +109,13 @@ export const Signup = ({ changeMode, trial }) => {
style={{
justifyContent: "flex-end",
paddingHorizontal: 20,
backgroundColor: colors.nav,
backgroundColor: colors.secondary.background,
marginBottom: 20,
borderBottomWidth: 1,
borderBottomColor: colors.border,
borderBottomColor: colors.primary.border,
alignSelf: deviceMode !== "mobile" ? "center" : undefined,
borderWidth: deviceMode !== "mobile" ? 1 : null,
borderColor: deviceMode !== "mobile" ? colors.border : null,
borderColor: deviceMode !== "mobile" ? colors.primary.border : null,
borderRadius: deviceMode !== "mobile" ? 20 : null,
marginTop: deviceMode !== "mobile" ? 50 : null,
width: deviceMode === "mobile" ? null : "50%",
@@ -130,7 +131,7 @@ export const Signup = ({ changeMode, trial }) => {
style={{
width: 100,
height: 5,
backgroundColor: colors.accent,
backgroundColor: colors.primary.accent,
borderRadius: 2,
marginRight: 7
}}
@@ -140,7 +141,7 @@ export const Signup = ({ changeMode, trial }) => {
style={{
width: 20,
height: 5,
backgroundColor: colors.nav,
backgroundColor: colors.secondary.background,
borderRadius: 2
}}
/>
@@ -161,7 +162,7 @@ export const Signup = ({ changeMode, trial }) => {
style={{
width: DDS.isTab ? "50%" : "100%",
paddingHorizontal: 20,
backgroundColor: colors.bg,
backgroundColor: colors.primary.background,
alignSelf: "center"
}}
>
@@ -230,7 +231,7 @@ export const Signup = ({ changeMode, trial }) => {
marginBottom: 25
}}
size={SIZE.xs}
color={colors.icon}
color={colors.secondary.paragraph}
>
By signing up, you agree to our{" "}
<Paragraph
@@ -241,7 +242,7 @@ export const Signup = ({ changeMode, trial }) => {
style={{
textDecorationLine: "underline"
}}
color={colors.accent}
color={colors.primary.accent}
>
Terms of Service{" "}
</Paragraph>
@@ -254,7 +255,7 @@ export const Signup = ({ changeMode, trial }) => {
style={{
textDecorationLine: "underline"
}}
color={colors.accent}
color={colors.primary.accent}
>
Privacy Policy.
</Paragraph>{" "}
@@ -287,9 +288,12 @@ export const Signup = ({ changeMode, trial }) => {
paddingVertical: 12
}}
>
<Paragraph size={SIZE.xs + 1} color={colors.icon}>
<Paragraph size={SIZE.xs + 1} color={colors.secondary.paragraph}>
Already have an account?{" "}
<Paragraph size={SIZE.xs + 1} style={{ color: colors.accent }}>
<Paragraph
size={SIZE.xs + 1}
style={{ color: colors.primary.accent }}
>
Login
</Paragraph>
</Paragraph>

View File

@@ -24,9 +24,9 @@ import useTimer from "../../hooks/use-timer";
import {
eSendEvent,
presentSheet,
ToastEvent
ToastManager
} from "../../services/event-manager";
import { useThemeStore } from "../../stores/use-theme-store";
import { useThemeColors } from "@notesnook/theme";
import { eCloseSheet } from "../../utils/events";
import { SIZE } from "../../utils/size";
import { Button } from "../ui/button";
@@ -39,7 +39,7 @@ import Paragraph from "../ui/typography/paragraph";
import { useCallback } from "react";
const TwoFactorVerification = ({ onMfaLogin, mfaInfo }) => {
const colors = useThemeStore((state) => state.colors);
const { colors } = useThemeColors();
const code = useRef();
const [currentMethod, setCurrentMethod] = useState({
method: mfaInfo?.primaryMethod,
@@ -138,7 +138,7 @@ const TwoFactorVerification = ({ onMfaLogin, mfaInfo }) => {
setSending(false);
} catch (e) {
setSending(false);
ToastEvent.error(e, "Error sending 2FA Code", "local");
ToastManager.error(e, "Error sending 2FA Code", "local");
}
}, [currentMethod.method, mfaInfo.token, seconds, sending, start]);
@@ -157,7 +157,7 @@ const TwoFactorVerification = ({ onMfaLogin, mfaInfo }) => {
}}
size={50}
name="key"
color={colors.accent}
color={colors.primary.accent}
/>
<Heading
style={{
@@ -214,6 +214,7 @@ const TwoFactorVerification = ({ onMfaLogin, mfaInfo }) => {
code.current = value;
//onNext();
}}
caretHidden
inputStyle={{
fontSize: SIZE.lg,
height: 60,
@@ -281,7 +282,7 @@ const TwoFactorVerification = ({ onMfaLogin, mfaInfo }) => {
marginRight: 10
}}
size={15}
color={colors.accent}
color={colors.primary.accent}
name={item.icon}
/>
<View

View File

@@ -19,7 +19,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
import { useRef, useState } from "react";
import { db } from "../../common/database";
import { eSendEvent, ToastEvent } from "../../services/event-manager";
import { eSendEvent, ToastManager } from "../../services/event-manager";
import { clearMessage } from "../../services/message";
import PremiumService from "../../services/premium";
import SettingsService from "../../services/settings";
@@ -33,7 +33,7 @@ export const LoginSteps = {
passwordAuth: 3
};
export const useLogin = (onFinishLogin) => {
export const useLogin = (onFinishLogin, sessionExpired = false) => {
const [error, setError] = useState(false);
const [loading, setLoading] = useState(false);
const setUser = useUserStore((state) => state.setUser);
@@ -48,7 +48,7 @@ export const useLogin = (onFinishLogin) => {
(!password.current && step === LoginSteps.passwordAuth) ||
(!email.current && step === LoginSteps.emailAuth)
) {
ToastEvent.show({
ToastManager.show({
heading: "All fields required",
message: "Fill all the fields and try again",
type: "error",
@@ -64,10 +64,12 @@ export const useLogin = (onFinishLogin) => {
const login = async () => {
if (!validateInfo() || error) return;
try {
if (loading) return;
setLoading(true);
switch (step) {
case LoginSteps.emailAuth: {
const mfaInfo = await db.user.authenticateEmail(email.current);
console.log("email auth", mfaInfo);
if (mfaInfo) {
TwoFactorVerification.present(async (mfa, callback) => {
try {
@@ -100,7 +102,12 @@ export const useLogin = (onFinishLogin) => {
break;
}
case LoginSteps.passwordAuth: {
await db.user.authenticatePassword(email.current, password.current);
await db.user.authenticatePassword(
email.current,
password.current,
null,
sessionExpired
);
finishLogin();
break;
}
@@ -114,7 +121,7 @@ export const useLogin = (onFinishLogin) => {
const finishWithError = async (e) => {
if (e.message === "invalid_grant") setStep(LoginSteps.emailAuth);
setLoading(false);
ToastEvent.show({
ToastManager.show({
heading: "Login failed",
message: e.message,
type: "error",
@@ -128,7 +135,7 @@ export const useLogin = (onFinishLogin) => {
PremiumService.setPremiumStatus();
setUser(user);
clearMessage();
ToastEvent.show({
ToastManager.show({
heading: "Login successful",
message: `Logged in as ${user.email}`,
type: "success",

View File

@@ -17,8 +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 { useThemeColors } from "@notesnook/theme";
import React, { useCallback, useEffect } from "react";
import { Keyboard, Platform, View } from "react-native";
import { Keyboard, View } from "react-native";
import Animated, {
Easing,
useAnimatedStyle,
@@ -35,12 +36,21 @@ import { SIZE, normalize } from "../../utils/size";
import NativeTooltip from "../../utils/tooltip";
import { PressableButton } from "../ui/pressable";
export const FloatingButton = ({
interface FloatingButtonProps {
title: string;
onPress: () => void;
color?: string;
shouldShow?: boolean;
alwaysVisible?: boolean;
}
const FloatingButton = ({
title,
onPress,
color = "accent",
shouldShow = false
}) => {
color,
alwaysVisible = false
}: FloatingButtonProps) => {
const { colors } = useThemeColors();
const deviceMode = useSettingStore((state) => state.deviceMode);
const selectionMode = useSelectionStore((state) => state.selectionMode);
const translate = useSharedValue(0);
@@ -58,12 +68,8 @@ export const FloatingButton = ({
};
});
useEffect(() => {
animate(selectionMode ? 150 : 0);
}, [animate, selectionMode]);
const animate = useCallback(
(toValue) => {
(toValue: number) => {
translate.value = withTiming(toValue, {
duration: 250,
easing: Easing.elastic(1)
@@ -72,39 +78,39 @@ export const FloatingButton = ({
[translate]
);
const onKeyboardHide = useCallback(async () => {
editorState().keyboardState = false;
if (deviceMode !== "mobile") return;
animate(0);
}, [animate, deviceMode]);
const onKeyboardShow = useCallback(async () => {
editorState().keyboardState = true;
if (deviceMode !== "mobile") return;
animate(150);
}, [animate, deviceMode]);
useEffect(() => {
animate(selectionMode ? 150 : 0);
}, [animate, selectionMode]);
useEffect(() => {
let sub1 = Keyboard.addListener("keyboardDidShow", onKeyboardShow);
let sub2 = Keyboard.addListener("keyboardDidHide", onKeyboardHide);
return () => {
sub1?.remove();
sub2?.remove();
const onKeyboardHide = () => {
editorState().keyboardState = false;
if (deviceMode !== "mobile") return;
animate(0);
};
}, [deviceMode, onKeyboardHide, onKeyboardShow]);
const paddings = {
ios: 20,
android: 20,
iPad: 20
};
return deviceMode !== "mobile" && !shouldShow ? null : (
const onKeyboardShow = () => {
editorState().keyboardState = true;
if (deviceMode !== "mobile") return;
animate(150);
};
const sub = [
Keyboard.addListener("keyboardDidShow", onKeyboardShow),
Keyboard.addListener("keyboardDidHide", onKeyboardHide)
];
return () => {
sub.forEach((sub) => sub?.remove?.());
};
}, [deviceMode, animate]);
return deviceMode !== "mobile" && !alwaysVisible ? null : (
<Animated.View
style={[
{
position: "absolute",
right: 12,
bottom: paddings[Platform.isPad ? "iPad" : Platform.OS],
bottom: 20,
zIndex: 10
},
animatedStyle
@@ -113,14 +119,15 @@ export const FloatingButton = ({
<PressableButton
testID={notesnook.buttons.add}
type="accent"
accentColor={color || "accent"}
accentText="light"
accentColor={color}
customStyle={{
...getElevationStyle(5),
borderRadius: 100
}}
onLongPress={(event) => {
NativeTooltip.show(event, title, NativeTooltip.POSITIONS.LEFT);
if (title) {
NativeTooltip.show(event, title, NativeTooltip.POSITIONS.LEFT);
}
}}
onPress={onPress}
>
@@ -134,7 +141,7 @@ export const FloatingButton = ({
>
<Icon
name={title === "Clear all trash" ? "delete" : "plus"}
color="white"
color={colors.primary.accentForeground}
size={SIZE.xxl}
/>
</View>
@@ -142,3 +149,5 @@ export const FloatingButton = ({
</Animated.View>
);
};
export { FloatingButton };

View File

@@ -1,62 +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 React from "react";
import { KeyboardAvoidingView, Platform, View } from "react-native";
import useGlobalSafeAreaInsets from "../../hooks/use-global-safe-area-insets";
import useIsFloatingKeyboard from "../../hooks/use-is-floating-keyboard";
import { useSettingStore } from "../../stores/use-setting-store";
import { Header } from "../header";
import SelectionHeader from "../selection-header";
export const Container = ({ children }) => {
const floating = useIsFloatingKeyboard();
const introCompleted = useSettingStore(
(state) => state.settings.introCompleted
);
const insets = useGlobalSafeAreaInsets();
return (
<KeyboardAvoidingView
behavior="padding"
enabled={Platform.OS === "ios" && !floating}
style={{
flex: 1
}}
>
<View
style={{
flex: 1,
overflow: "hidden",
paddingTop: Platform.OS === "android" ? 0 : insets.top,
paddingBottom: Platform.OS === "android" ? 0 : insets.bottom
}}
>
{!introCompleted ? null : (
<>
<SelectionHeader />
<Header title="Header" screen="Header" />
</>
)}
{children}
</View>
</KeyboardAvoidingView>
);
};
export default Container;

View File

@@ -0,0 +1,59 @@
/*
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 React, { PropsWithChildren, useEffect, useRef, useState } from "react";
import { Dimensions, Platform, View, useWindowDimensions } from "react-native";
import useGlobalSafeAreaInsets from "../../hooks/use-global-safe-area-insets";
import useKeyboard from "../../hooks/use-keyboard";
export const Container = ({ children }: PropsWithChildren) => {
const insets = useGlobalSafeAreaInsets();
const keyboard = useKeyboard();
const [height, setHeight] = useState(0);
const windowHeightRef = useRef(Dimensions.get("window").height);
const { height: windowHeight } = useWindowDimensions();
useEffect(() => {
if (windowHeight !== windowHeightRef.current) {
setHeight(0);
windowHeightRef.current = windowHeight;
}
}, [windowHeight]);
return (
<View
style={{
overflow: "hidden",
paddingTop: Platform.OS === "android" ? 0 : insets.top,
paddingBottom: Platform.OS === "android" ? 0 : insets.bottom,
height: height || "100%",
width: "100%"
}}
onLayout={(event) => {
if (!keyboard.keyboardShown) {
setHeight(event.nativeEvent.layout.height);
}
}}
>
{children}
</View>
);
};
export default Container;

View File

@@ -20,21 +20,21 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
import React from "react";
import { View } from "react-native";
import { useMessageStore } from "../../stores/use-message-store";
import { useThemeStore } from "../../stores/use-theme-store";
import { COLORS_NOTE } from "../../utils/color-scheme";
import { hexToRGBA } from "../../utils/color-scheme/utils";
import { useThemeColors } from "@notesnook/theme";
import { ColorValues } from "../../utils/colors";
import { hexToRGBA } from "../../utils/colors";
export const DefaultPlaceholder = ({ color }: { color: string }) => {
const colors = useThemeStore((state) => state.colors);
const { colors } = useThemeColors();
const message = useMessageStore((state) => state.message);
const annoucements = useMessageStore((state) => state.announcements);
const hasAnnoucements = annoucements.length > 0;
const shadeColor = color
? hexToRGBA(
COLORS_NOTE[color?.toLowerCase() as keyof typeof COLORS_NOTE],
ColorValues[color?.toLowerCase() as keyof typeof ColorValues],
0.15
)
: colors.shade;
: colors.primary.shade;
return (
<View
@@ -50,7 +50,7 @@ export const DefaultPlaceholder = ({ color }: { color: string }) => {
height: 100,
borderRadius: 10,
marginBottom: 20,
backgroundColor: colors.nav,
backgroundColor: colors.secondary.background,
padding: 12
}}
>
@@ -58,7 +58,7 @@ export const DefaultPlaceholder = ({ color }: { color: string }) => {
style={{
width: 150,
height: 20,
backgroundColor: colors.transGray,
backgroundColor: colors.primary.hover,
borderRadius: 5,
marginBottom: 10
}}
@@ -67,7 +67,7 @@ export const DefaultPlaceholder = ({ color }: { color: string }) => {
style={{
width: 250,
height: 14,
backgroundColor: colors.transGray,
backgroundColor: colors.primary.hover,
borderRadius: 5
}}
/>
@@ -110,7 +110,7 @@ export const DefaultPlaceholder = ({ color }: { color: string }) => {
style={{
width: 150,
height: 12,
backgroundColor: colors.nav,
backgroundColor: colors.secondary.background,
borderRadius: 5,
marginBottom: 10
}}
@@ -119,7 +119,7 @@ export const DefaultPlaceholder = ({ color }: { color: string }) => {
style={{
width: 250,
height: 16,
backgroundColor: colors.nav,
backgroundColor: colors.secondary.background,
borderRadius: 5
}}
/>
@@ -131,7 +131,7 @@ export const DefaultPlaceholder = ({ color }: { color: string }) => {
style={{
width: "100%",
height: 30,
backgroundColor: colors.nav,
backgroundColor: colors.secondary.background,
borderRadius: 10,
marginBottom: 20,
padding: 5,
@@ -158,7 +158,7 @@ export const DefaultPlaceholder = ({ color }: { color: string }) => {
style={{
width: 15,
height: 15,
backgroundColor: colors.transGray,
backgroundColor: colors.primary.hover,
borderRadius: 100,
marginRight: 10
}}
@@ -167,7 +167,7 @@ export const DefaultPlaceholder = ({ color }: { color: string }) => {
style={{
width: 60,
height: 15,
backgroundColor: colors.transGray,
backgroundColor: colors.primary.hover,
borderRadius: 3
}}
/>
@@ -178,7 +178,7 @@ export const DefaultPlaceholder = ({ color }: { color: string }) => {
style={{
width: 200,
height: 16,
backgroundColor: colors.nav,
backgroundColor: colors.secondary.background,
borderRadius: 5
}}
/>
@@ -186,7 +186,7 @@ export const DefaultPlaceholder = ({ color }: { color: string }) => {
style={{
width: "85%",
height: 13,
backgroundColor: colors.nav,
backgroundColor: colors.secondary.background,
borderRadius: 5,
marginTop: 10
}}
@@ -202,7 +202,7 @@ export const DefaultPlaceholder = ({ color }: { color: string }) => {
style={{
width: 50,
height: 10,
backgroundColor: colors.nav,
backgroundColor: colors.secondary.background,
borderRadius: 5
}}
/>
@@ -210,7 +210,7 @@ export const DefaultPlaceholder = ({ color }: { color: string }) => {
style={{
width: 60,
height: 10,
backgroundColor: colors.nav,
backgroundColor: colors.secondary.background,
borderRadius: 5,
marginLeft: 10
}}

View File

@@ -21,7 +21,7 @@ import React from "react";
import { ViewProps } from "react-native";
import Animated, { FadeOutUp } from "react-native-reanimated";
import { useDelayLayout } from "../../hooks/use-delay-layout";
import { useThemeStore } from "../../stores/use-theme-store";
import { useThemeColors } from "@notesnook/theme";
import { DefaultPlaceholder } from "./default-placeholder";
import { SettingsPlaceholder } from "./settings-placeholder";
@@ -42,9 +42,9 @@ export default function DelayLayout({
animated = true,
...props
}: IDelayLayoutProps) {
const colors = useThemeStore((state) => state.colors);
const { colors } = useThemeColors();
const loading = useDelayLayout(
!props.delay || props.delay < 300 ? 300 : props.delay
!props.delay || props.delay < 300 ? 0 : props.delay
);
const Placeholder = placeholder[props.type || "default"];
@@ -52,12 +52,12 @@ export default function DelayLayout({
<Animated.View
exiting={animated ? FadeOutUp : undefined}
style={{
backgroundColor: colors.bg,
backgroundColor: colors.primary.background,
flex: 1,
paddingTop: 20
}}
>
<Placeholder color={props.color || colors.accent} />
<Placeholder color={props.color || colors.primary.accent} />
</Animated.View>
) : (
<>{props.children}</>

View File

@@ -19,10 +19,10 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
import React from "react";
import { View } from "react-native";
import { useThemeStore } from "../../stores/use-theme-store";
import { useThemeColors } from "@notesnook/theme";
export const SettingsPlaceholder = () => {
const colors = useThemeStore((state) => state.colors);
const { colors } = useThemeColors();
return (
<View>
@@ -30,7 +30,7 @@ export const SettingsPlaceholder = () => {
style={{
width: 100,
height: 12,
backgroundColor: colors.shade,
backgroundColor: colors.primary.shade,
borderRadius: 5,
marginLeft: 12,
marginBottom: 12
@@ -51,7 +51,7 @@ export const SettingsPlaceholder = () => {
style={{
width: 40,
height: 40,
backgroundColor: colors.transGray,
backgroundColor: colors.primary.hover,
borderRadius: 100,
marginRight: 20
}}
@@ -61,7 +61,7 @@ export const SettingsPlaceholder = () => {
style={{
width: 150,
height: 12,
backgroundColor: colors.nav,
backgroundColor: colors.secondary.background,
borderRadius: 5,
marginBottom: 10
}}
@@ -70,7 +70,7 @@ export const SettingsPlaceholder = () => {
style={{
width: 250,
height: 16,
backgroundColor: colors.nav,
backgroundColor: colors.secondary.background,
borderRadius: 5
}}
/>
@@ -93,7 +93,7 @@ export const SettingsPlaceholder = () => {
style={{
width: 40,
height: 40,
backgroundColor: colors.shade,
backgroundColor: colors.primary.shade,
borderRadius: 100,
marginRight: 20
}}
@@ -103,7 +103,7 @@ export const SettingsPlaceholder = () => {
style={{
width: 150,
height: 12,
backgroundColor: colors.nav,
backgroundColor: colors.secondary.background,
borderRadius: 5,
marginBottom: 10
}}
@@ -112,7 +112,7 @@ export const SettingsPlaceholder = () => {
style={{
width: 250,
height: 16,
backgroundColor: colors.nav,
backgroundColor: colors.secondary.background,
borderRadius: 5
}}
/>
@@ -122,7 +122,7 @@ export const SettingsPlaceholder = () => {
style={{
width: 40,
height: 20,
backgroundColor: colors.nav,
backgroundColor: colors.secondary.background,
borderRadius: 100,
marginLeft: 15,
alignItems: "flex-end",
@@ -134,7 +134,7 @@ export const SettingsPlaceholder = () => {
style={{
width: 15,
height: 15,
backgroundColor: colors.shade,
backgroundColor: colors.primary.shade,
borderRadius: 100,
marginLeft: 15
}}

View File

@@ -17,15 +17,15 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { useThemeColors } from "@notesnook/theme";
import React from "react";
import { useNoteStore } from "../../stores/use-notes-store";
import { useThemeStore } from "../../stores/use-theme-store";
import { AnnouncementDialog } from "../announcements";
import AuthModal from "../auth/auth-modal";
import { SessionExpired } from "../auth/session-expired";
import { Dialog } from "../dialog";
import { AddTopicDialog } from "../dialogs/add-topic";
import JumpToSectionDialog from "../dialogs/jump-to-section";
import { LoadingDialog } from "../dialogs/loading";
import PDFPreview from "../dialogs/pdf-preview";
import ResultDialog from "../dialogs/result";
import { VaultDialog } from "../dialogs/vault";
import ImagePreview from "../image-preview";
@@ -36,17 +36,16 @@ import SheetProvider from "../sheet-provider";
import RateAppSheet from "../sheets/rate-app";
import RecoveryKeySheet from "../sheets/recovery-key";
import RestoreDataSheet from "../sheets/restore-data";
import PDFPreview from "../dialogs/pdf-preview";
import { AppLockPassword } from "../dialogs/applock-password";
const DialogProvider = () => {
const colors = useThemeStore((state) => state.colors);
const loading = useNoteStore((state) => state.loading);
const { colors } = useThemeColors();
return (
<>
<AppLockPassword />
<LoadingDialog />
<Dialog context="global" />
<AddTopicDialog colors={colors} />
<PremiumDialog colors={colors} />
<AuthModal colors={colors} />
<MergeConflicts />
@@ -58,10 +57,11 @@ const DialogProvider = () => {
<VaultDialog colors={colors} />
<RateAppSheet />
<ImagePreview />
{loading ? null : <Expiring />}
<Expiring />
<AnnouncementDialog />
<SessionExpired />
<PDFPreview />
<JumpToSectionDialog />
</>
);
};

View File

@@ -21,11 +21,9 @@ import { eSendEvent } from "../../services/event-manager";
import {
eCloseActionSheet,
eCloseAddNotebookDialog,
eCloseAddTopicDialog,
eCloseMoveNoteDialog,
eOpenActionSheet,
eOpenAddNotebookDialog,
eOpenAddTopicDialog,
eOpenMoveNoteDialog
} from "../../utils/events";
@@ -52,9 +50,3 @@ export const AddNotebookEvent = (notebook) => {
export const HideAddNotebookEvent = (notebook) => {
eSendEvent(eCloseAddNotebookDialog, notebook);
};
export const AddTopicEvent = (topic) => {
eSendEvent(eOpenAddTopicDialog, topic);
};
export const HideAddTopicEvent = (notebook) => {
eSendEvent(eCloseAddTopicDialog, notebook);
};

View File

@@ -1,143 +0,0 @@
/*
This file is part of the Notesnook project (https://notesnook.com/)
Copyright (C) 2023 Streetwriters (Private) Limited
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import React, { useEffect } from "react";
import {
KeyboardAvoidingView,
Modal,
Platform,
SafeAreaView,
StyleSheet,
TouchableOpacity,
View
} from "react-native";
import useIsFloatingKeyboard from "../../hooks/use-is-floating-keyboard";
import { useSettingStore } from "../../stores/use-setting-store";
import { BouncingView } from "../ui/transitions/bouncing-view";
const BaseDialog = ({
visible,
onRequestClose,
children,
onShow,
animation = "fade",
premium,
statusBarTranslucent = true,
transparent,
centered = true,
bottom = false,
background = null,
animated = true,
bounce = true,
closeOnTouch = true,
useSafeArea = true,
avoidKeyboardResize = false
}) => {
const floating = useIsFloatingKeyboard();
useEffect(() => {
return () => {
useSettingStore.getState().setSheetKeyboardHandler(true);
};
}, []);
const Wrapper = useSafeArea ? SafeAreaView : View;
return (
<Modal
visible={visible}
transparent={true}
animated
statusBarTranslucent={statusBarTranslucent}
supportedOrientations={[
"portrait",
"portrait-upside-down",
"landscape",
"landscape-left",
"landscape-right"
]}
onShow={() => {
if (onShow) {
onShow();
useSettingStore.getState().setSheetKeyboardHandler(false);
}
}}
animationType={animation}
onRequestClose={() => {
if (!closeOnTouch) return null;
useSettingStore.getState().setSheetKeyboardHandler(true);
onRequestClose && onRequestClose();
}}
>
<Wrapper
style={{
backgroundColor: background
? background
: transparent
? "transparent"
: "rgba(0,0,0,0.3)"
}}
>
<KeyboardAvoidingView
enabled={!floating && Platform.OS === "ios" && !avoidKeyboardResize}
behavior="padding"
>
<BouncingView
duration={400}
animated={animated}
initialScale={bounce ? 0.9 : 1}
style={[
styles.backdrop,
{
justifyContent: centered
? "center"
: bottom
? "flex-end"
: "flex-start"
}
]}
>
<TouchableOpacity
onPress={closeOnTouch ? onRequestClose : null}
style={styles.overlayButton}
/>
{premium}
{children}
</BouncingView>
</KeyboardAvoidingView>
</Wrapper>
</Modal>
);
};
const styles = StyleSheet.create({
backdrop: {
width: "100%",
height: "100%",
justifyContent: "center",
alignItems: "center"
},
overlayButton: {
width: "100%",
height: "100%",
position: "absolute"
}
});
export default BaseDialog;

View File

@@ -0,0 +1,198 @@
/*
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 React, { PropsWithChildren, useEffect, useRef, useState } from "react";
import {
ColorValue,
KeyboardAvoidingView,
Modal,
Platform,
SafeAreaView,
StyleSheet,
TouchableOpacity,
View
} from "react-native";
import useIsFloatingKeyboard from "../../hooks/use-is-floating-keyboard";
import { useSettingStore } from "../../stores/use-setting-store";
import { BouncingView } from "../ui/transitions/bouncing-view";
import { ScopedThemeProvider } from "@notesnook/theme";
import SettingsService from "../../services/settings";
import { useUserStore } from "../../stores/use-user-store";
import { useAppState } from "../../hooks/use-app-state";
export interface BaseDialogProps extends PropsWithChildren {
animation?: "fade" | "none" | "slide" | undefined;
visible: boolean;
onRequestClose?: () => void;
onShow?: () => void;
premium?: boolean;
statusBarTranslucent?: boolean;
transparent?: boolean;
centered?: boolean;
bottom?: boolean;
background?: string | ColorValue;
animated?: boolean;
bounce?: boolean;
closeOnTouch?: boolean;
useSafeArea?: boolean;
avoidKeyboardResize?: boolean;
enableSheetKeyboardHandler?: boolean;
}
const BaseDialog = ({
visible,
onRequestClose,
children,
onShow,
animation = "fade",
premium,
statusBarTranslucent = true,
transparent,
centered = true,
bottom = false,
animated = true,
bounce = true,
closeOnTouch = true,
useSafeArea = true,
avoidKeyboardResize = false,
enableSheetKeyboardHandler,
background
}: BaseDialogProps) => {
const floating = useIsFloatingKeyboard();
const appState = useAppState();
const lockEvents = useRef(false);
const [internalVisible, setIntervalVisible] = useState(true);
useEffect(() => {
return () => {
useSettingStore.getState().setSheetKeyboardHandler(true);
};
}, []);
useEffect(() => {
if (useUserStore.getState().disableAppLockRequests) return;
if (SettingsService.canLockAppInBackground()) {
if (appState === "background") {
setIntervalVisible(false);
if (useUserStore.getState().appLocked) {
lockEvents.current = true;
const unsub = useUserStore.subscribe((state) => {
if (!state.appLocked) {
setIntervalVisible(true);
unsub();
setTimeout(() => {
lockEvents.current = false;
});
}
});
}
}
}
}, [appState]);
const Wrapper = useSafeArea ? SafeAreaView : View;
return (
<ScopedThemeProvider value="dialog">
<Modal
visible={visible && internalVisible}
transparent={true}
statusBarTranslucent={statusBarTranslucent}
supportedOrientations={[
"portrait",
"portrait-upside-down",
"landscape",
"landscape-left",
"landscape-right"
]}
onShow={() => {
if (lockEvents.current) return;
if (onShow) {
onShow();
if (!enableSheetKeyboardHandler) {
useSettingStore.getState().setSheetKeyboardHandler(false);
}
}
}}
animationType={animation}
onRequestClose={() => {
if (lockEvents.current) return;
if (!closeOnTouch) return null;
useSettingStore.getState().setSheetKeyboardHandler(true);
onRequestClose && onRequestClose();
}}
>
<Wrapper
style={{
backgroundColor: background
? background
: transparent
? "transparent"
: "rgba(0,0,0,0.3)"
}}
>
<KeyboardAvoidingView
enabled={!floating && Platform.OS === "ios" && !avoidKeyboardResize}
behavior="padding"
>
<BouncingView
duration={400}
animated={animated}
initialScale={bounce ? 0.9 : 1}
style={[
styles.backdrop,
{
alignItems: centered ? "center" : undefined,
justifyContent: centered
? "center"
: bottom
? "flex-end"
: "flex-start"
}
]}
>
<TouchableOpacity
onPress={closeOnTouch ? onRequestClose : undefined}
style={styles.overlayButton}
/>
{premium}
{children}
</BouncingView>
</KeyboardAvoidingView>
</Wrapper>
</Modal>
</ScopedThemeProvider>
);
};
const styles = StyleSheet.create({
backdrop: {
width: "100%",
height: "100%",
justifyContent: "center"
},
overlayButton: {
width: "100%",
height: "100%",
position: "absolute"
}
});
export default BaseDialog;

View File

@@ -21,7 +21,7 @@ import React from "react";
import { ActivityIndicator, StyleSheet, View } from "react-native";
import Icon from "react-native-vector-icons/MaterialCommunityIcons";
import { notesnook } from "../../../e2e/test.ids";
import { useThemeStore } from "../../stores/use-theme-store";
import { useThemeColors } from "@notesnook/theme";
import { SIZE } from "../../utils/size";
import { Button } from "../ui/button";
import Paragraph from "../ui/typography/paragraph";
@@ -35,14 +35,14 @@ const DialogButtons = ({
doneText,
positiveType
}) => {
const colors = useThemeStore((state) => state.colors);
const { colors } = useThemeColors();
return (
<View
style={[
styles.container,
{
backgroundColor: colors.nav,
backgroundColor: colors.secondary.background,
height: 60,
borderBottomRightRadius: 10,
borderBottomLeftRadius: 10,
@@ -51,7 +51,7 @@ const DialogButtons = ({
]}
>
{loading ? (
<ActivityIndicator color={colors.accent} size={SIZE.lg} />
<ActivityIndicator color={colors.primary.accent} size={SIZE.lg} />
) : doneText ? (
<View
style={{
@@ -60,11 +60,11 @@ const DialogButtons = ({
}}
>
<Icon
color={colors.accent}
color={colors.primary.accent}
name="check-circle-outline"
size={SIZE.md}
/>
<Paragraph color={colors.accent}>{" " + doneText}</Paragraph>
<Paragraph color={colors.primary.accent}>{" " + doneText}</Paragraph>
</View>
) : (
<View />

View File

@@ -18,25 +18,36 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import React from "react";
import { View } from "react-native";
import { View, ViewProps } from "react-native";
import { DDS } from "../../services/device-detection";
import { useThemeStore } from "../../stores/use-theme-store";
import { useThemeColors } from "@notesnook/theme";
import { getElevationStyle } from "../../utils/elevation";
const DialogContainer = ({ width, height, ...restProps }) => {
const colors = useThemeStore((state) => state.colors);
const DialogContainer = ({
width,
height,
style,
...restProps
}: ViewProps & {
width?: any;
height?: any;
}) => {
const { colors } = useThemeColors();
return (
<View
{...restProps}
style={{
...getElevationStyle(5),
width: width || DDS.isTab ? 500 : "85%",
maxHeight: height || 450,
borderRadius: 10,
backgroundColor: colors.bg,
paddingTop: 12
}}
style={[
{
...getElevationStyle(5),
width: width || DDS.isTab ? 500 : "85%",
maxHeight: height || 450,
borderRadius: 10,
backgroundColor: colors.primary.background,
paddingTop: 12
},
style
]}
/>
);
};

View File

@@ -19,7 +19,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
import React from "react";
import { Text, View, ViewStyle } from "react-native";
import { useThemeStore } from "../../stores/use-theme-store";
import { useThemeColors } from "@notesnook/theme";
import { SIZE } from "../../utils/size";
import { Button } from "../ui/button";
import { PressableButtonProps } from "../ui/pressable";
@@ -54,7 +54,7 @@ const DialogHeader = ({
titlePart,
style
}: DialogHeaderProps) => {
const colors = useThemeStore((state) => state.colors);
const { colors } = useThemeColors();
return (
<>
@@ -86,7 +86,7 @@ const DialogHeader = ({
>
{title}{" "}
{titlePart ? (
<Text style={{ color: colors.accent }}>{titlePart}</Text>
<Text style={{ color: colors.primary.accent }}>{titlePart}</Text>
) : null}
</Heading>
@@ -114,7 +114,7 @@ const DialogHeader = ({
maxWidth: centered ? "90%" : "100%",
alignSelf: centered ? "center" : "flex-start"
}}
color={paragraphColor || colors.icon}
color={paragraphColor || colors.secondary.paragraph}
>
{paragraph}
</Paragraph>

View File

@@ -25,7 +25,7 @@ type DialogInfo = {
paragraph?: string;
positiveText?: string;
negativeText?: string;
positivePress?: (value: unknown) => void;
positivePress?: (value: any) => void;
onClose?: () => void;
positiveType?:
| "transparent"
@@ -41,7 +41,9 @@ type DialogInfo = {
input: boolean;
inputPlaceholder: string;
defaultValue: string;
context: "global" | "local";
// eslint-disable-next-line @typescript-eslint/ban-types
context: "global" | "local" | (string & {});
secureTextEntry?: boolean;
};
export function presentDialog(data: Partial<DialogInfo>): void {

View File

@@ -24,7 +24,7 @@ import {
eSubscribeEvent,
eUnSubscribeEvent
} from "../../services/event-manager";
import { useThemeStore } from "../../stores/use-theme-store";
import { useThemeColors } from "@notesnook/theme";
import { getElevationStyle } from "../../utils/elevation";
import { eCloseSimpleDialog, eOpenSimpleDialog } from "../../utils/events";
import { sleep } from "../../utils/time";
@@ -38,7 +38,7 @@ import { useCallback } from "react";
import { Button } from "../ui/button";
export const Dialog = ({ context = "global" }) => {
const colors = useThemeStore((state) => state.colors);
const { colors } = useThemeColors();
const [visible, setVisible] = useState(false);
const [inputValue, setInputValue] = useState(null);
const inputRef = useRef();
@@ -51,7 +51,7 @@ export const Dialog = ({ context = "global" }) => {
onClose: () => {},
positiveType: "transparent",
icon: null,
paragraphColor: colors.pri,
paragraphColor: colors.primary.paragraph,
input: false,
inputPlaceholder: "Enter some text",
defaultValue: "",
@@ -62,16 +62,6 @@ export const Dialog = ({ context = "global" }) => {
}
});
useEffect(() => {
eSubscribeEvent(eOpenSimpleDialog, show);
eSubscribeEvent(eCloseSimpleDialog, hide);
return () => {
eUnSubscribeEvent(eOpenSimpleDialog, show);
eUnSubscribeEvent(eCloseSimpleDialog, hide);
};
}, [show]);
const onPressPositive = async () => {
if (dialogInfo.positivePress) {
inputRef.current?.blur();
@@ -97,6 +87,16 @@ export const Dialog = ({ context = "global" }) => {
[context]
);
useEffect(() => {
eSubscribeEvent(eOpenSimpleDialog, show);
eSubscribeEvent(eCloseSimpleDialog, hide);
return () => {
eUnSubscribeEvent(eOpenSimpleDialog, show);
eUnSubscribeEvent(eCloseSimpleDialog, hide);
};
}, [show]);
const hide = () => {
setInputValue(null);
setVisible(false);
@@ -115,7 +115,7 @@ export const Dialog = ({ context = "global" }) => {
width: DDS.isTab ? 400 : "85%",
maxHeight: 450,
borderRadius: 5,
backgroundColor: colors.bg,
backgroundColor: colors.primary.background,
paddingTop: 12
};

View File

@@ -1,190 +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 React, { createRef } from "react";
import { View } from "react-native";
import { useMenuStore } from "../../../stores/use-menu-store";
import {
eSendEvent,
eSubscribeEvent,
eUnSubscribeEvent,
ToastEvent
} from "../../../services/event-manager";
import Navigation from "../../../services/navigation";
import { db } from "../../../common/database";
import {
eCloseAddTopicDialog,
eOnTopicSheetUpdate,
eOpenAddTopicDialog
} from "../../../utils/events";
import { sleep } from "../../../utils/time";
import BaseDialog from "../../dialog/base-dialog";
import DialogButtons from "../../dialog/dialog-buttons";
import DialogContainer from "../../dialog/dialog-container";
import DialogHeader from "../../dialog/dialog-header";
import Input from "../../ui/input";
import Seperator from "../../ui/seperator";
import { Toast } from "../../toast";
import { useRelationStore } from "../../../stores/use-relation-store";
export class AddTopicDialog extends React.Component {
constructor(props) {
super(props);
this.state = {
visible: false,
titleFocused: false,
loading: false
};
this.ref = createRef();
this.title;
this.titleRef = createRef();
this.notebook = null;
this.toEdit = null;
}
addNewTopic = async () => {
try {
if (!this.title || this.title?.trim() === "") {
ToastEvent.show({
heading: "Topic title is required",
type: "error",
context: "local"
});
return;
}
if (!this.toEdit) {
await db.notebooks.notebook(this.notebook.id).topics.add(this.title);
} else {
let topic = this.toEdit;
topic.title = this.title;
await db.notebooks.notebook(topic.notebookId).topics.add(topic);
}
this.close();
setTimeout(() => {
Navigation.queueRoutesForUpdate();
useMenuStore.getState().setMenuPins();
});
eSendEvent(eOnTopicSheetUpdate);
useRelationStore.getState().update();
} catch (e) {
console.error(e);
}
};
componentDidMount() {
eSubscribeEvent(eOpenAddTopicDialog, this.open);
eSubscribeEvent(eCloseAddTopicDialog, this.close);
}
componentWillUnmount() {
eUnSubscribeEvent(eOpenAddTopicDialog, this.open);
eUnSubscribeEvent(eCloseAddTopicDialog, this.close);
}
open = async ({ notebookId, toEdit }) => {
let id = notebookId;
if (id) {
this.notebook = await db.notebooks.notebook(id).data;
}
this.toEdit = toEdit;
if (this.toEdit) {
this.title = this.toEdit.title;
}
this.setState({
visible: true
});
};
close = () => {
this.title = null;
this.notebook = null;
this.toEdit = null;
this.setState({
visible: false
});
};
render() {
const { visible } = this.state;
if (!visible) return null;
return (
<BaseDialog
onShow={async () => {
if (this.toEdit) {
this.titleRef.current?.setNativeProps({
text: this.toEdit.title
});
}
this.ref.current?.animateNextTransition();
await sleep(300);
this.titleRef.current?.focus();
}}
bounce={false}
statusBarTranslucent={false}
visible={true}
onRequestClose={this.close}
>
<DialogContainer>
<DialogHeader
icon="book-outline"
title={this.toEdit ? "Edit topic" : "New topic"}
paragraph={
this.toEdit
? "Edit title of the topic"
: "Add a new topic in " + this.notebook.title
}
padding={12}
/>
<Seperator half />
<View
style={{
paddingHorizontal: 12,
zIndex: 10
}}
>
<Input
fwdRef={this.titleRef}
testID="input-title"
onChangeText={(value) => {
this.title = value;
}}
blurOnSubmit={false}
placeholder="Enter title"
onSubmit={() => this.addNewTopic()}
returnKeyLabel="Done"
returnKeyType="done"
/>
</View>
<DialogButtons
positiveTitle={this.toEdit ? "Save" : "Add"}
onPressNegative={() => this.close()}
onPressPositive={() => this.addNewTopic()}
/>
</DialogContainer>
<Toast context="local" />
</BaseDialog>
);
}
}

View File

@@ -0,0 +1,358 @@
/*
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 { useThemeColors } from "@notesnook/theme";
import React, { useEffect, useRef, useState } from "react";
import { TextInput, View } from "react-native";
import {
clearAppLockVerificationCipher,
setAppLockVerificationCipher,
validateAppLockPassword
} from "../../../common/database/encryption";
import BiometicService from "../../../services/biometrics";
import { DDS } from "../../../services/device-detection";
import {
ToastManager,
eSendEvent,
eSubscribeEvent
} from "../../../services/event-manager";
import SettingsService from "../../../services/settings";
import { useSettingStore } from "../../../stores/use-setting-store";
import { getElevationStyle } from "../../../utils/elevation";
import {
eCloseAppLocKPasswordDailog,
eOpenAppLockPasswordDialog
} from "../../../utils/events";
import { SIZE } from "../../../utils/size";
import { sleep } from "../../../utils/time";
import BaseDialog from "../../dialog/base-dialog";
import DialogButtons from "../../dialog/dialog-buttons";
import DialogHeader from "../../dialog/dialog-header";
import { Toast } from "../../toast";
import { IconButton } from "../../ui/icon-button";
import Input from "../../ui/input";
import Seperator from "../../ui/seperator";
export const AppLockPassword = () => {
const { colors } = useThemeColors();
const [mode, setMode] = useState<"create" | "change" | "remove">("create");
const [keyboardType, setKeyboardType] = useState<"pin" | "password">(
useSettingStore.getState().settings.applockKeyboardType === "default"
? "password"
: "pin"
);
const [visible, setVisible] = useState(false);
const currentPasswordInputRef = useRef<TextInput>(null);
const passwordInputRef = useRef<TextInput>(null);
const confirmPasswordInputRef = useRef<TextInput>(null);
const values = useRef<{
currentPassword?: string;
password?: string;
confirmPassword?: string;
}>({});
const [secureTextEntry, setSecureTextEntry] = useState(true);
useEffect(() => {
const subs = [
eSubscribeEvent(
eOpenAppLockPasswordDialog,
(mode: "create" | "change" | "remove") => {
setMode(mode);
setVisible(true);
}
),
eSubscribeEvent(eCloseAppLocKPasswordDailog, () => {
values.current = {};
setVisible(false);
})
];
return () => {
subs.forEach((sub) => sub?.unsubscribe());
};
}, []);
const close = () => {
values.current = {};
setVisible(false);
};
return !visible ? null : (
<BaseDialog
onShow={async () => {
await sleep(100);
if (mode !== "change") {
passwordInputRef.current?.focus();
} else {
currentPasswordInputRef.current?.focus();
}
}}
statusBarTranslucent={false}
onRequestClose={close}
visible={visible}
>
<View
style={{
...getElevationStyle(10),
width: DDS.isTab ? 350 : "85%",
borderRadius: 10,
backgroundColor: colors.primary.background,
paddingTop: 12
}}
>
<DialogHeader
title={
mode === "change"
? `Change app lock ${keyboardType}`
: mode === "remove"
? `Remove app lock ${keyboardType}`
: `Set up app lock ${keyboardType}`
}
paragraph={
mode === "change"
? `Change app lock ${keyboardType}`
: mode === "remove"
? `Remove app lock ${keyboardType}`
: `Set up a custom app lock ${keyboardType} to unlock the app`
}
icon="shield"
padding={12}
/>
<Seperator half />
<View
style={{
paddingHorizontal: 12
}}
>
{mode === "change" ? (
<Input
fwdRef={currentPasswordInputRef}
autoCapitalize="none"
onChangeText={(value) => {
values.current.currentPassword = value;
}}
onSubmit={() => {
passwordInputRef.current?.focus();
}}
defaultValue={values.current.currentPassword}
autoComplete="password"
returnKeyLabel="Next"
keyboardType={keyboardType === "pin" ? "number-pad" : "default"}
returnKeyType="next"
secureTextEntry={secureTextEntry}
placeholder={`Current ${keyboardType}`}
/>
) : null}
<Input
fwdRef={passwordInputRef}
autoCapitalize="none"
onChangeText={(value) => {
values.current.password = value;
}}
onSubmit={() => {
confirmPasswordInputRef.current?.focus();
}}
defaultValue={values.current.password}
keyboardType={keyboardType === "pin" ? "number-pad" : "default"}
autoComplete="password"
returnKeyLabel={mode !== "remove" ? "Next" : "Remove"}
returnKeyType={mode !== "remove" ? "next" : "done"}
secureTextEntry={secureTextEntry}
buttonLeft={
<IconButton
name={keyboardType === "password" ? "numeric" : "keyboard"}
onPress={() => {
setKeyboardType(
keyboardType === "password" ? "pin" : "password"
);
setSecureTextEntry(false);
setImmediate(() => {
setSecureTextEntry(true);
});
}}
customStyle={{
width: 25,
height: 25,
marginRight: 5
}}
size={SIZE.lg}
/>
}
placeholder={
mode === "change"
? `New ${keyboardType}`
: `${keyboardType === "pin" ? "Pin" : "Password"}`
}
/>
{mode !== "remove" ? (
<Input
fwdRef={confirmPasswordInputRef}
autoCapitalize="none"
onChangeText={(value) => {
values.current.confirmPassword = value;
}}
onSubmit={() => {
confirmPasswordInputRef.current?.focus();
}}
defaultValue={values.current.confirmPassword}
keyboardType={keyboardType === "pin" ? "number-pad" : "default"}
customValidator={() => values.current.password || ""}
validationType="confirmPassword"
autoComplete="password"
returnKeyLabel="Done"
returnKeyType="done"
secureTextEntry={secureTextEntry}
placeholder={`Confirm ${keyboardType}`}
/>
) : null}
</View>
<DialogButtons
onPressNegative={close}
onPressPositive={async () => {
if (mode === "create") {
if (!values.current.password || !values.current.confirmPassword) {
ToastManager.error(
new Error("All inputs are required"),
undefined,
"local"
);
return;
}
if (values.current.password !== values.current.confirmPassword) {
ToastManager.error(
new Error(
`${
keyboardType === "pin" ? "Pin" : "Password"
} does not match`
),
undefined,
"local"
);
return;
}
await setAppLockVerificationCipher(values.current.password);
SettingsService.setProperty("appLockHasPasswordSecurity", true);
} else if (mode === "change") {
if (
!values.current.currentPassword ||
!values.current.password ||
!values.current.confirmPassword
) {
ToastManager.error(
new Error("All inputs are required"),
undefined,
"local"
);
return;
}
if (values.current.password !== values.current.confirmPassword) {
ToastManager.error(
new Error(
`${
keyboardType === "pin" ? "Pin" : "Password"
} does not match`
),
undefined,
"local"
);
return;
}
const isCurrentPasswordCorrect = await validateAppLockPassword(
values.current.currentPassword
);
if (!isCurrentPasswordCorrect) {
ToastManager.error(
new Error(
`${keyboardType === "pin" ? "Pin" : "Password"} incorrect`
),
undefined,
"local"
);
return;
}
SettingsService.setProperty("appLockHasPasswordSecurity", true);
await setAppLockVerificationCipher(values.current.password);
} else if (mode === "remove") {
if (!values.current.password) {
ToastManager.error(
new Error("All inputs are required"),
undefined,
"local"
);
return;
}
const isCurrentPasswordCorrect = await validateAppLockPassword(
values.current.password
);
if (!isCurrentPasswordCorrect) {
ToastManager.error(
new Error(
`${keyboardType === "pin" ? "Pin" : "Password"} incorrect`
),
"local"
);
return;
}
clearAppLockVerificationCipher();
SettingsService.setProperty("appLockHasPasswordSecurity", false);
if (
!(await BiometicService.isBiometryAvailable()) ||
SettingsService.getProperty("biometricsAuthEnabled") === false
) {
SettingsService.setProperty("appLockEnabled", false);
ToastManager.show({
message: "App lock disabled",
type: "success"
});
}
}
SettingsService.setProperty(
"applockKeyboardType",
keyboardType === "password" ? "default" : "numeric"
);
close();
}}
positiveTitle="Save"
negativeTitle="Cancel"
positiveType="transparent"
loading={false}
doneText=""
/>
</View>
<Toast context="local" />
</BaseDialog>
);
};
AppLockPassword.present = (mode: "create" | "change" | "remove") => {
eSendEvent(eOpenAppLockPasswordDialog, mode);
};

View File

@@ -0,0 +1,196 @@
/*
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 } from "@notesnook/core";
import { useThemeColors } from "@notesnook/theme";
import React, { useRef, useState } from "react";
import { TextInput, View } from "react-native";
import ColorPickerWheel from "react-native-wheel-color-picker";
import { db } from "../../../common/database";
import { useStoredRef } from "../../../hooks/use-stored-ref";
import { ToastManager } from "../../../services/event-manager";
import { useMenuStore } from "../../../stores/use-menu-store";
import { useRelationStore } from "../../../stores/use-relation-store";
import { useSettingStore } from "../../../stores/use-setting-store";
import BaseDialog from "../../dialog/base-dialog";
import DialogContainer from "../../dialog/dialog-container";
import { Toast } from "../../toast";
import { Button } from "../../ui/button";
import Input from "../../ui/input";
import { PressableButton } from "../../ui/pressable";
const HEX_COLOR_REGEX_ALPHA =
/^#(?:(?:[\da-fA-F]{3}){1,2}|(?:[\da-fA-F]{4}){1,2})$/;
const ColorPicker = ({
visible,
setVisible,
onColorAdded
}: {
visible: boolean;
setVisible: (value: boolean) => void;
onColorAdded: (color: Color) => void;
}) => {
const [selectedColor, setSelectedColor] = useState<string>();
const { colors } = useThemeColors();
const inputRef = useRef<TextInput>(null);
const title = useRef<string>();
const colorRef = useStoredRef("color-ref", "#f0f0f0");
return (
<BaseDialog
visible={visible}
onRequestClose={() => {
title.current = undefined;
setVisible(false);
useSettingStore.getState().setSheetKeyboardHandler(true);
}}
statusBarTranslucent={false}
centered
>
<Toast context="color-picker" />
<DialogContainer
style={{
paddingTop: 0,
maxHeight: 600
}}
>
<View>
<View
style={{
width: "100%",
height: 250,
backgroundColor: selectedColor,
borderRadius: 10
}}
>
<ColorPickerWheel
onColorChangeComplete={(color) => {
if (color === selectedColor) return;
colorRef.current = color;
setSelectedColor(color);
inputRef.current?.setNativeProps({
placeholder: color,
text: color
});
}}
useNativeLayout={true}
autoResetSlider={true}
color={colorRef.current}
sliderSize={30}
thumbSize={25}
shadeWheelThumb
shadeSliderThumb={true}
swatchesOnly={false}
swatches={false}
useNativeDriver={true}
/>
</View>
<View
style={{
paddingHorizontal: 12
}}
>
<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)) {
colorRef.current = value;
setSelectedColor(value);
}
}}
defaultValue={colorRef.current}
/>
<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;
}}
defaultValue={title.current}
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`)
);
const id = await db.colors.add({
title: title.current,
colorCode: selectedColor
});
useRelationStore.getState().update();
useMenuStore.getState().setColorNotes();
setVisible(false);
const color = await db.colors.color(id);
if (color) {
onColorAdded?.(color);
}
}}
type={selectedColor ? "grayAccent" : "grayBg"}
width="100%"
/>
</View>
</View>
</DialogContainer>
</BaseDialog>
);
};
export default ColorPicker;

View File

@@ -17,15 +17,22 @@ 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, { useEffect, useState } from "react";
import { ScrollView, View } from "react-native";
import { GroupHeader, Item, VirtualizedGrouping } from "@notesnook/core";
import { useThemeColors } from "@notesnook/theme";
import React, {
RefObject,
useCallback,
useEffect,
useRef,
useState
} from "react";
import { ActivityIndicator, FlatList, ScrollView, View } from "react-native";
import { DDS } from "../../../services/device-detection";
import {
eSubscribeEvent,
eUnSubscribeEvent
} from "../../../services/event-manager";
import { useMessageStore } from "../../../stores/use-message-store";
import { useThemeStore } from "../../../stores/use-theme-store";
import { getElevationStyle } from "../../../utils/elevation";
import {
eCloseJumpToDialog,
@@ -36,27 +43,49 @@ import { SIZE } from "../../../utils/size";
import BaseDialog from "../../dialog/base-dialog";
import { PressableButton } from "../../ui/pressable";
import Paragraph from "../../ui/typography/paragraph";
import { useCallback } from "react";
const offsets = [];
let timeout = null;
const JumpToSectionDialog = ({ scrollRef, data, type }) => {
const colors = useThemeStore((state) => state.colors);
const JumpToSectionDialog = () => {
const scrollRef = useRef<RefObject<FlatList>>();
const [data, setData] = useState<VirtualizedGrouping<Item>>();
const { colors } = useThemeColors();
const notes = data;
const [visible, setVisible] = useState(false);
const [currentIndex, setCurrentIndex] = useState(null);
const [currentIndex, setCurrentIndex] = useState(0);
const currentScrollPosition = useRef(0);
const [loading, setLoading] = useState(false);
const onPress = (item) => {
let ind = notes.findIndex(
(i) => i.title === item.title && i.type === "header"
);
scrollRef.current?.scrollToIndex({
index: ind,
const [groups, setGroups] = useState<
{
index: number;
group: GroupHeader;
}[]
>();
const offsets = useRef<number[]>([]);
const onPress = (item: { index: number; group: GroupHeader }) => {
scrollRef.current?.current?.scrollToIndex({
index: item.index,
animated: true
});
close();
};
const open = useCallback(
({
data,
ref
}: {
data: VirtualizedGrouping<Item>;
ref: RefObject<FlatList>;
}) => {
setLoading(true);
setData(data);
scrollRef.current = ref;
setVisible(true);
},
[]
);
useEffect(() => {
eSubscribeEvent(eOpenJumpToDialog, open);
eSubscribeEvent(eCloseJumpToDialog, close);
@@ -69,55 +98,46 @@ const JumpToSectionDialog = ({ scrollRef, data, type }) => {
};
}, [open]);
const onScroll = (data) => {
let y = data.y;
if (timeout) {
clearTimeout(timeout);
timeout = null;
}
timeout = setTimeout(() => {
let index = offsets.findIndex((o, i) => o <= y && offsets[i + 1] > y);
setCurrentIndex(index);
}, 200);
const onScroll = (data: { x: number; y: number }) => {
currentScrollPosition.current = data.y;
};
const open = useCallback(
(_type) => {
if (_type !== type) return;
setVisible(true);
},
[type]
);
const close = () => {
setVisible(false);
};
useEffect(() => {
loadOffsets();
}, [loadOffsets, notes]);
const loadOffsets = useCallback(() => {
notes
.filter((i) => i.type === "header")
.map((item, index) => {
const loadGroupsAndOffsets = useCallback(() => {
notes?.groups?.().then((groups) => {
setGroups(groups);
offsets.current = [];
groups.map((item, index) => {
let offset = 35 * index;
let ind = notes.findIndex(
(i) => i.title === item.title && i.type === "header"
);
let messageState = useMessageStore.getState().message;
let msgOffset = messageState?.visible ? 60 : 10;
ind = ind + 1;
ind = ind - (index + 1);
offset = offset + ind * 100 + msgOffset;
offsets.push(offset);
let groupIndex = item.index;
const messageState = useMessageStore.getState().message;
const msgOffset = messageState?.visible ? 60 : 10;
groupIndex = groupIndex + 1;
groupIndex = groupIndex - (index + 1);
offset = offset + groupIndex * 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]);
return !visible ? null : (
<BaseDialog
onShow={() => {
loadOffsets();
loadGroupsAndOffsets();
}}
onRequestClose={close}
visible={true}
@@ -126,7 +146,7 @@ const JumpToSectionDialog = ({ scrollRef, data, type }) => {
style={{
...getElevationStyle(5),
width: DDS.isTab ? 500 : "85%",
backgroundColor: colors.bg,
backgroundColor: colors.primary.background,
zIndex: 100,
bottom: 20,
maxHeight: "65%",
@@ -136,27 +156,34 @@ const JumpToSectionDialog = ({ scrollRef, data, type }) => {
paddingTop: 30
}}
>
<ScrollView
style={{
maxHeight: "100%"
}}
>
<View
{loading ? (
<ActivityIndicator
size={SIZE.lg}
color={colors.primary.accent}
style={{
flexDirection: "row",
flexWrap: "wrap",
alignSelf: "center",
justifyContent: "center",
paddingBottom: 20
marginBottom: 20
}}
/>
) : (
<ScrollView
style={{
maxHeight: "100%"
}}
>
{notes
.filter((i) => i.type === "header")
.map((item, index) => {
return item.title ? (
<View
style={{
flexDirection: "row",
flexWrap: "wrap",
alignSelf: "center",
justifyContent: "center",
paddingBottom: 20
}}
>
{groups?.map((item, index) => {
return (
<PressableButton
key={item.title}
onPress={() => onPress(item, index)}
key={item.group.id}
onPress={() => onPress(item)}
type={currentIndex === index ? "accent" : "transparent"}
customStyle={{
minWidth: "20%",
@@ -164,26 +191,29 @@ const JumpToSectionDialog = ({ scrollRef, data, type }) => {
paddingHorizontal: 12,
margin: 5,
borderRadius: 100,
height: 25,
height: 30,
marginVertical: 10
}}
>
<Paragraph
size={SIZE.sm}
color={
currentIndex === index ? colors.light : colors.accent
currentIndex === index
? colors.static.white
: colors.primary.paragraph
}
style={{
textAlign: "center"
}}
>
{item.title}
{item.group.title}
</Paragraph>
</PressableButton>
) : null;
);
})}
</View>
</ScrollView>
</View>
</ScrollView>
)}
</View>
</BaseDialog>
);

View File

@@ -18,7 +18,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import React from "react";
import { View } from "react-native";
import { useThemeStore } from "../../../stores/use-theme-store";
import { useThemeColors } from "@notesnook/theme";
import BaseDialog from "../../dialog/base-dialog";
import { ProgressBarComponent } from "../../ui/svg/lazy";
import { useEffect } from "react";
@@ -30,7 +30,7 @@ import { useState } from "react";
import { eCloseLoading, eOpenLoading } from "../../../utils/events";
export const LoadingDialog = () => {
const colors = useThemeStore((state) => state.colors);
const { colors } = useThemeColors();
const [visible, setVisible] = useState(false);
useEffect(() => {
@@ -58,7 +58,7 @@ export const LoadingDialog = () => {
style={{
width: "100%",
height: "100%",
backgroundColor: colors.bg,
backgroundColor: colors.primary.background,
justifyContent: "center",
alignItems: "center"
}}
@@ -77,8 +77,8 @@ export const LoadingDialog = () => {
useNativeDriver
indeterminate
indeterminateAnimationDuration={2000}
unfilledColor={colors.nav}
color={colors.accent}
unfilledColor={colors.secondary.background}
color={colors.primary.accent}
borderWidth={0}
/>
</View>

View File

@@ -17,7 +17,7 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import React, { useEffect, useRef, useState } from "react";
import React, { useCallback, useEffect, useRef, useState } from "react";
import { Dimensions, TextInput, View } from "react-native";
import {
addOrientationListener,
@@ -33,7 +33,7 @@ import {
eSubscribeEvent,
eUnSubscribeEvent
} from "../../../services/event-manager";
import { useThemeStore } from "../../../stores/use-theme-store";
import { useThemeColors } from "@notesnook/theme";
import { SIZE } from "../../../utils/size";
import { Dialog } from "../../dialog";
import BaseDialog from "../../dialog/base-dialog";
@@ -64,7 +64,7 @@ const usePDFSnapshot = (attachment) => {
};
const PDFPreview = () => {
const colors = useThemeStore((state) => state.colors);
const { colors } = useThemeColors();
const [visible, setVisible] = useState(false);
const [pdfSource, setPDFSource] = useState();
const [loading, setLoading] = useState(false);
@@ -85,7 +85,7 @@ const PDFPreview = () => {
return () => {
eUnSubscribeEvent("PDFPreview", open);
};
}, []);
}, [open]);
const onOrientationChange = (o) => {
if (o.includes("LANDSCAPE")) {
@@ -102,23 +102,26 @@ const PDFPreview = () => {
};
}, []);
const open = async (attachment) => {
setVisible(true);
setLoading(true);
setTimeout(async () => {
setAttachment(attachment);
let hash = attachment.metadata.hash;
if (!hash) return;
const uri = await downloadAttachment(hash, false, {
silent: true,
cache: true
});
const path = `${cacheDir}/${uri}`;
snapshotValue.current = snapshot.current;
setPDFSource("file://" + path);
setLoading(false);
}, 100);
};
const open = useCallback(
async (attachment) => {
setVisible(true);
setLoading(true);
setTimeout(async () => {
setAttachment(attachment);
let hash = attachment.hash;
if (!hash) return;
const uri = await downloadAttachment(hash, false, {
silent: true,
cache: true
});
const path = `${cacheDir}/${uri}`;
snapshotValue.current = snapshot.current;
setPDFSource("file://" + path);
setLoading(false);
}, 100);
},
[snapshot]
);
const close = () => {
setPDFSource(null);
@@ -130,7 +133,7 @@ const PDFPreview = () => {
if (error?.message === "Password required or incorrect password.") {
await sleep(300);
presentDialog({
context: attachment?.metadata?.hash,
context: attachment?.hash,
input: true,
inputPlaceholder: "Enter password",
positiveText: "Unlock",
@@ -151,8 +154,8 @@ const PDFPreview = () => {
return (
visible && (
<BaseDialog animation="fade" visible={true} onRequestClose={close}>
<SheetProvider context={attachment?.metadata?.hash} />
<Dialog context={attachment?.metadata?.hash} />
<SheetProvider context={attachment?.hash} />
<Dialog context={attachment?.hash} />
<View
style={{
@@ -172,7 +175,7 @@ const PDFPreview = () => {
>
<ProgressBarComponent
indeterminate={!progress}
color={colors.accent}
color={colors.primary.accent}
borderColor="transparent"
progress={parseInt(progress?.value || "100") / 100}
/>
@@ -180,7 +183,7 @@ const PDFPreview = () => {
style={{
marginTop: 10
}}
color={colors.light}
color={colors.static.white}
>
Loading {`${progress?.percent ? `(${progress?.percent})` : ""}`}
... Please wait
@@ -205,7 +208,7 @@ const PDFPreview = () => {
}}
>
<IconButton
color={colors.light}
color={colors.static.white}
name="arrow-left"
onPress={close}
customStyle={{
@@ -226,7 +229,7 @@ const PDFPreview = () => {
ref={inputRef}
defaultValue={currentPage + ""}
style={{
color: colors.pri,
color: colors.primary.paragraph,
padding: 0,
paddingTop: 0,
paddingBottom: 0,
@@ -234,7 +237,7 @@ const PDFPreview = () => {
marginBottom: 0,
paddingVertical: 0,
height: 25,
backgroundColor: colors.nav,
backgroundColor: colors.secondary.background,
width: 40,
textAlign: "center",
marginRight: 4,
@@ -249,7 +252,7 @@ const PDFPreview = () => {
}}
blurOnSubmit
/>
<Paragraph color={colors.light}>/{numPages}</Paragraph>
<Paragraph color={colors.static.white}>/{numPages}</Paragraph>
</View>
<View
@@ -258,10 +261,10 @@ const PDFPreview = () => {
}}
>
<IconButton
color={colors.light}
color={colors.static.white}
name="download"
onPress={() => {
downloadAttachment(attachment.metadata.hash, false);
downloadAttachment(attachment.hash, false);
}}
/>
</View>

View File

@@ -24,7 +24,7 @@ import {
eSubscribeEvent,
eUnSubscribeEvent
} from "../../../services/event-manager";
import { useThemeStore } from "../../../stores/use-theme-store";
import { useThemeColors } from "@notesnook/theme";
import { getElevationStyle } from "../../../utils/elevation";
import { eCloseResultDialog, eOpenResultDialog } from "../../../utils/events";
import { SIZE } from "../../../utils/size";
@@ -36,7 +36,7 @@ import Paragraph from "../../ui/typography/paragraph";
import { ProFeatures } from "./pro-features";
const ResultDialog = () => {
const colors = useThemeStore((state) => state.colors);
const { colors } = useThemeColors();
const [visible, setVisible] = useState(false);
const [dialogData, setDialogData] = useState({
title: "Thank you for signing up!",
@@ -72,7 +72,7 @@ const ResultDialog = () => {
width: DDS.isTab ? 350 : "85%",
maxHeight: 500,
borderRadius: 10,
backgroundColor: colors.bg,
backgroundColor: colors.primary.background,
paddingTop: 20,
justifyContent: "center",
alignItems: "center"
@@ -94,7 +94,7 @@ const ResultDialog = () => {
</Heading>
<Paragraph
color={colors.icon}
color={colors.secondary.paragraph}
size={SIZE.md}
style={{
alignSelf: "center",
@@ -121,7 +121,7 @@ const ResultDialog = () => {
<Seperator />
<View
style={{
backgroundColor: colors.nav,
backgroundColor: colors.secondary.background,
width: "100%",
borderBottomRightRadius: 10,
borderBottomLeftRadius: 10,

View File

@@ -21,7 +21,7 @@ import React from "react";
import { View } from "react-native";
import Icon from "react-native-vector-icons/MaterialCommunityIcons";
import { eSendEvent } from "../../../services/event-manager";
import { useThemeStore } from "../../../stores/use-theme-store";
import { useThemeColors } from "@notesnook/theme";
import {
eCloseSheet,
eCloseResultDialog,
@@ -31,7 +31,7 @@ import { SIZE } from "../../../utils/size";
import { sleep } from "../../../utils/time";
import Paragraph from "../../ui/typography/paragraph";
export const ProFeatures = ({ count = 6 }) => {
const colors = useThemeStore((state) => state.colors);
const { colors } = useThemeColors();
return (
<>
@@ -48,7 +48,7 @@ export const ProFeatures = ({ count = 6 }) => {
content: "Instantly sync to unlimited devices"
},
{
content: "A private vault to keep everything imporant always locked"
content: "A private vault to keep everything important always locked"
},
{
content:
@@ -73,7 +73,7 @@ export const ProFeatures = ({ count = 6 }) => {
justifyContent: "flex-start"
}}
>
<Icon size={SIZE.lg} color={colors.accent} name="check" />
<Icon size={SIZE.lg} color={colors.primary.accent} name="check" />
<Paragraph style={{ marginLeft: 5, flexShrink: 1 }}>
{item.content}
</Paragraph>
@@ -89,7 +89,7 @@ export const ProFeatures = ({ count = 6 }) => {
size={SIZE.xs}
style={{
textDecorationLine: "underline",
color: colors.icon
color: colors.secondary.paragraph
}}
>
See all features included in Notesnook Pro

View File

@@ -27,14 +27,12 @@ import { editorController } from "../../../screens/editor/tiptap/utils";
import BiometricService from "../../../services/biometrics";
import { DDS } from "../../../services/device-detection";
import {
ToastManager,
eSendEvent,
eSubscribeEvent,
eUnSubscribeEvent,
ToastEvent
eUnSubscribeEvent
} from "../../../services/event-manager";
import Navigation from "../../../services/navigation";
import SearchService from "../../../services/search";
import { convertNoteToText } from "../../../utils/note-to-text";
import { getElevationStyle } from "../../../utils/elevation";
import {
eClearEditor,
@@ -45,6 +43,7 @@ import {
} from "../../../utils/events";
import { deleteItems } from "../../../utils/functions";
import { tabBarRef } from "../../../utils/global-refs";
import { convertNoteToText } from "../../../utils/note-to-text";
import { sleep } from "../../../utils/time";
import BaseDialog from "../../dialog/base-dialog";
import DialogButtons from "../../dialog/dialog-buttons";
@@ -55,10 +54,6 @@ import Input from "../../ui/input";
import Seperator from "../../ui/seperator";
import Paragraph from "../../ui/typography/paragraph";
let Keychain;
const passInputRef = createRef();
const confirmPassRef = createRef();
const changePassInputRef = createRef();
export class VaultDialog extends Component {
constructor(props) {
super(props);
@@ -86,8 +81,14 @@ export class VaultDialog extends Component {
description: null,
clearVault: false,
deleteVault: false,
deleteAll: false
deleteAll: false,
noteLocked: false
};
this.passInputRef = createRef();
this.confirmPassRef = createRef();
this.changePassInputRef = createRef();
this.password = null;
this.confirmPassword = null;
this.newPassword = null;
@@ -99,7 +100,7 @@ export class VaultDialog extends Component {
? "Revoke Vault Fingerprint Unlock"
: this.state.changePassword
? "Change Vault Password"
: this.state.note.locked
: this.state.noteLocked
? this.state.deleteNote
? "Delete note"
: this.state.share
@@ -120,7 +121,7 @@ export class VaultDialog extends Component {
? "Setup a new password for the vault."
: this.state.permanant
? "Enter password to remove note from vault."
: this.state.note.locked
: this.state.noteLocked
? this.state.deleteNote
? "Unlock note to delete it. If biometrics are not working, you can enter device pin to unlock vault."
: this.state.share
@@ -148,9 +149,6 @@ export class VaultDialog extends Component {
* @param {import('../../../services/event-manager').vaultType} data
*/
open = async (data) => {
if (!Keychain) {
Keychain = require("react-native-keychain");
}
let biometry = await BiometricService.isBiometryAvailable();
let available = false;
let fingerprint = await BiometricService.hasInternetCredentials("nn_vault");
@@ -177,7 +175,8 @@ export class VaultDialog extends Component {
title: data.title,
description: data.description,
clearVault: data.clearVault,
deleteVault: data.deleteVault
deleteVault: data.deleteVault,
noteLocked: data.item && (await db.vaults.itemExists(data.item))
});
if (
@@ -199,7 +198,7 @@ export class VaultDialog extends Component {
close = () => {
if (this.state.loading) {
ToastEvent.show({
ToastManager.show({
heading: this.state.title,
message: "Please wait and do not close the app.",
type: "success",
@@ -212,7 +211,6 @@ export class VaultDialog extends Component {
this.password = null;
this.confirmPassword = null;
SearchService.updateAndSearch();
this.setState({
visible: false,
note: {},
@@ -236,7 +234,7 @@ export class VaultDialog extends Component {
if (this.state.loading) return;
if (!this.password) {
ToastEvent.show({
ToastManager.show({
heading: "Password not entered",
message: "Enter a password for the vault and try again.",
type: "error",
@@ -245,7 +243,7 @@ export class VaultDialog extends Component {
return;
}
if (this.password && this.password.length < 3) {
ToastEvent.show({
ToastManager.show({
heading: "Password too short",
message: "Password must be longer than 3 characters.",
type: "error",
@@ -257,7 +255,7 @@ export class VaultDialog extends Component {
if (!this.state.novault) {
if (this.password !== this.confirmPassword) {
ToastEvent.show({
ToastManager.show({
heading: "Passwords do not match",
type: "error",
context: "local"
@@ -283,7 +281,7 @@ export class VaultDialog extends Component {
if (this.state.biometricUnlock) {
this._enrollFingerprint(this.newPassword);
}
ToastEvent.show({
ToastManager.show({
heading: "Vault password updated successfully",
type: "success",
context: "global"
@@ -295,17 +293,19 @@ export class VaultDialog extends Component {
loading: false
});
if (e.message === db.vault.ERRORS.wrongPassword) {
ToastEvent.show({
ToastManager.show({
heading: "Incorrect password",
message: "Please enter the correct password and try again",
type: "error",
context: "local"
});
} else {
ToastManager.error(e);
}
});
} else if (this.state.locked) {
if (!this.password || this.password.trim() === 0) {
ToastEvent.show({
ToastManager.show({
heading: "Incorrect password",
message: "Please enter the correct password and try again",
type: "error",
@@ -316,7 +316,7 @@ export class VaultDialog extends Component {
});
return;
}
if (this.state.note.locked) {
if (this.state.noteLocked) {
await this._unlockNote();
} else {
db.vault
@@ -355,7 +355,7 @@ export class VaultDialog extends Component {
});
this.close();
} else {
ToastEvent.show({
ToastManager.show({
heading: "Account password incorrect",
message: "Please enter correct password for your account.",
type: "error",
@@ -382,7 +382,7 @@ export class VaultDialog extends Component {
this.close();
eSendEvent("vaultUpdated");
} catch (e) {
ToastEvent.show({
ToastManager.show({
heading: "Vault password incorrect",
message: "Please enter correct password to clear vault.",
type: "error",
@@ -396,7 +396,7 @@ export class VaultDialog extends Component {
async _lockNote() {
if (!this.password || this.password.trim() === 0) {
ToastEvent.show({
ToastManager.show({
heading: "Incorrect password",
type: "error",
context: "local"
@@ -408,7 +408,7 @@ export class VaultDialog extends Component {
eSendEvent(eClearEditor);
}
this.close();
ToastEvent.show({
ToastManager.show({
message: "Note locked successfully",
type: "error",
context: "local"
@@ -421,7 +421,7 @@ export class VaultDialog extends Component {
async _unlockNote() {
if (!this.password || this.password.trim() === 0) {
ToastEvent.show({
ToastManager.show({
heading: "Incorrect password",
message: "Please enter the correct password and try again",
type: "error",
@@ -442,6 +442,7 @@ export class VaultDialog extends Component {
if (this.state.biometricUnlock && !this.state.isBiometryEnrolled) {
await this._enrollFingerprint(this.password);
}
if (this.state.goToEditor) {
this._openInEditor(note);
} else if (this.state.share) {
@@ -452,14 +453,13 @@ export class VaultDialog extends Component {
await this._copyNote(note);
}
} catch (e) {
console.log(e);
this._takeErrorAction(e);
}
};
async _deleteNote() {
try {
await db.vault.remove(this.state.note.id, this.password);
await deleteItems(this.state.note);
await deleteItems([this.state.note.id], "note");
this.close();
} catch (e) {
this._takeErrorAction(e);
@@ -467,44 +467,40 @@ export class VaultDialog extends Component {
}
async _enrollFingerprint(password) {
try {
this.setState(
{
loading: true
},
async () => {
try {
await db.vault.unlock(password);
await BiometricService.storeCredentials(password);
this.setState({
loading: false
});
eSendEvent("vaultUpdated");
ToastEvent.show({
heading: "Biometric unlocking enabled!",
message: "Now you can unlock notes in vault with biometrics.",
type: "success",
context: "global"
});
this.close();
} catch (e) {
ToastEvent.show({
heading: "Incorrect password",
message:
"Please enter the correct vault password to enable biometrics.",
type: "error",
context: "local"
});
this.setState({
loading: false
});
return;
}
this.setState(
{
loading: true
},
async () => {
try {
await db.vault.unlock(password);
await BiometricService.storeCredentials(password);
this.setState({
loading: false
});
eSendEvent("vaultUpdated");
ToastManager.show({
heading: "Biometric unlocking enabled!",
message: "Now you can unlock notes in vault with biometrics.",
type: "success",
context: "global"
});
this.close();
} catch (e) {
this.close();
ToastManager.show({
heading: "Incorrect password",
message:
"Please enter the correct vault password to enable biometrics.",
type: "error",
context: "local"
});
this.setState({
loading: false
});
}
);
} catch (e) {
this._takeErrorAction(e);
}
}
);
}
async _createVault() {
@@ -521,7 +517,7 @@ export class VaultDialog extends Component {
this.setState({
loading: false
});
ToastEvent.show({
ToastManager.show({
heading: "Note added to vault",
type: "success",
context: "global"
@@ -529,7 +525,7 @@ export class VaultDialog extends Component {
this.close();
} else {
eSendEvent("vaultUpdated");
ToastEvent.show({
ToastManager.show({
heading: "Vault created successfully",
type: "success",
context: "global"
@@ -542,8 +538,8 @@ export class VaultDialog extends Component {
db.vault
.remove(this.state.note.id, this.password)
.then(() => {
ToastEvent.show({
heading: "Note permanantly unlocked.",
ToastManager.show({
heading: "Note permanently unlocked.",
type: "success",
context: "global"
});
@@ -557,7 +553,9 @@ export class VaultDialog extends Component {
_openInEditor(note) {
this.close();
InteractionManager.runAfterInteractions(async () => {
eSendEvent(eOnLoadNote, note);
eSendEvent(eOnLoadNote, {
item: note
});
if (!DDS.isTab) {
tabBarRef.current?.goToPage(1);
}
@@ -566,7 +564,7 @@ export class VaultDialog extends Component {
async _copyNote(note) {
Clipboard.setString(await convertNoteToText(note));
ToastEvent.show({
ToastManager.show({
heading: "Note copied",
type: "success",
message: "Note has been copied to clipboard!",
@@ -598,7 +596,7 @@ export class VaultDialog extends Component {
visible: true
});
setTimeout(() => {
ToastEvent.show({
ToastManager.show({
heading: "Incorrect password",
type: "error",
context: "local"
@@ -613,13 +611,13 @@ export class VaultDialog extends Component {
try {
await BiometricService.resetCredentials();
eSendEvent("vaultUpdated");
ToastEvent.show({
ToastManager.show({
heading: "Biometric unlocking disabled!",
type: "success",
context: "global"
});
} catch (e) {
ToastEvent.show({
ToastManager.show({
heading: "Failed to disable Biometric unlocking.",
message: e.message,
type: "success",
@@ -671,7 +669,8 @@ export class VaultDialog extends Component {
<BaseDialog
onShow={async () => {
await sleep(100);
passInputRef.current?.focus();
this.passInputRef.current?.focus();
}}
statusBarTranslucent={false}
onRequestClose={this.close}
@@ -682,7 +681,7 @@ export class VaultDialog extends Component {
...getElevationStyle(5),
width: DDS.isTab ? 350 : "85%",
borderRadius: 10,
backgroundColor: colors.bg,
backgroundColor: colors.primary.background,
paddingTop: 12
}}
>
@@ -706,7 +705,7 @@ export class VaultDialog extends Component {
!this.state.revokeFingerprintAccess ? (
<>
<Input
fwdRef={passInputRef}
fwdRef={this.passInputRef}
editable={!loading}
autoCapitalize="none"
testID={notesnook.ids.dialogs.vault.pwd}
@@ -723,7 +722,7 @@ export class VaultDialog extends Component {
}
onSubmit={() => {
changePassword
? changePassInputRef.current?.focus()
? this.confirmPassRef.current?.focus()
: this.onPress;
}}
autoComplete="password"
@@ -775,7 +774,7 @@ export class VaultDialog extends Component {
<>
<Seperator half />
<Input
ref={changePassInputRef}
ref={this.confirmPassRef}
editable={!loading}
testID={notesnook.ids.dialogs.vault.changePwd}
autoCapitalize="none"
@@ -795,7 +794,7 @@ export class VaultDialog extends Component {
{!novault ? (
<View>
<Input
fwdRef={passInputRef}
fwdRef={this.passInputRef}
autoCapitalize="none"
testID={notesnook.ids.dialogs.vault.pwd}
onChangeText={(value) => {
@@ -806,13 +805,13 @@ export class VaultDialog extends Component {
returnKeyType="next"
secureTextEntry
onSubmit={() => {
confirmPassRef.current?.focus();
this.confirmPassRef.current?.focus();
}}
placeholder="Password"
/>
<Input
fwdRef={confirmPassRef}
fwdRef={this.confirmPassRef}
autoCapitalize="none"
testID={notesnook.ids.dialogs.vault.pwdAlt}
secureTextEntry
@@ -867,7 +866,12 @@ export class VaultDialog extends Component {
icon="fingerprint"
width="100%"
title="Biometric unlocking"
type={this.state.biometricUnlock ? "transparent" : "gray"}
iconColor={
this.state.biometricUnlock
? colors.selected.icon
: colors.primary.icon
}
type={this.state.biometricUnlock ? "transparent" : "selected"}
/>
) : null}
</View>
@@ -890,7 +894,7 @@ export class VaultDialog extends Component {
? "Revoke"
: changePassword
? "Change"
: note.locked
: this.state.noteLocked
? deleteNote
? "Delete"
: share

View File

@@ -39,9 +39,9 @@ class ExceptionHandler extends React.Component<{
error: Error | null;
hasError: boolean;
} = {
hasError: false,
error: null
};
hasError: false,
error: null
};
static getDerivedStateFromError(error: Error) {
return { hasError: true, error: error };
}
@@ -69,7 +69,7 @@ class ExceptionHandler extends React.Component<{
this.props.component
)}
defaultTitle={this.state.error?.message}
issueTitle="An exception occured"
issueTitle="An exception occurred"
/>
<Dialog />
</SafeAreaView>

View File

@@ -17,42 +17,73 @@ 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, useEffect, useState } from "react";
import { Platform, StyleSheet, View } from "react-native";
import useGlobalSafeAreaInsets from "../../hooks/use-global-safe-area-insets";
import { SearchBar } from "../../screens/search/search-bar";
import {
eSubscribeEvent,
eUnSubscribeEvent
} from "../../services/event-manager";
import useNavigationStore from "../../stores/use-navigation-store";
import useNavigationStore, {
RouteName
} from "../../stores/use-navigation-store";
import { useSelectionStore } from "../../stores/use-selection-store";
import { useThemeStore } from "../../stores/use-theme-store";
import { eScrollEvent } from "../../utils/events";
import { LeftMenus } from "./left-menus";
import { RightMenus } from "./right-menus";
import { Title } from "./title";
const _Header = () => {
const colors = useThemeStore((state) => state.colors);
type HeaderRightButton = {
title: string;
onPress: () => void;
};
export const Header = ({
renderedInRoute,
onLeftMenuButtonPress,
title,
titleHiddenOnRender,
headerRightButtons,
id,
accentColor,
isBeta,
canGoBack,
onPressDefaultRightButton,
hasSearch,
onSearch
}: {
onLeftMenuButtonPress?: () => void;
renderedInRoute: RouteName;
id?: string;
title: string;
headerRightButtons?: HeaderRightButton[];
titleHiddenOnRender?: boolean;
accentColor?: string;
isBeta?: boolean;
canGoBack?: boolean;
onPressDefaultRightButton?: () => void;
hasSearch?: boolean;
onSearch?: () => void;
}) => {
const { colors } = useThemeColors();
const insets = useGlobalSafeAreaInsets();
const [hide, setHide] = useState(true);
const [borderHidden, setBorderHidden] = useState(true);
const selectionMode = useSelectionStore((state) => state.selectionMode);
const currentScreen = useNavigationStore(
(state) => state.currentScreen?.name
);
const isFocused = useNavigationStore((state) => state.focusedRouteId === id);
const onScroll = useCallback(
(data) => {
(data: { x: number; y: number; id?: string; route: string }) => {
if (data.route !== renderedInRoute || data.id !== id) return;
if (data.y > 150) {
if (!hide) return;
setHide(false);
if (!borderHidden) return;
setBorderHidden(false);
} else {
if (hide) return;
setHide(true);
if (borderHidden) return;
setBorderHidden(true);
}
},
[hide]
[borderHidden, id, renderedInRoute]
);
useEffect(() => {
@@ -60,39 +91,54 @@ const _Header = () => {
return () => {
eUnSubscribeEvent(eScrollEvent, onScroll);
};
}, [hide, onScroll]);
}, [borderHidden, onScroll]);
return selectionMode ? null : (
return selectionMode && isFocused ? null : (
<>
<View
style={[
styles.container,
{
marginTop: Platform.OS === "android" ? insets.top : null,
backgroundColor: colors.bg,
backgroundColor: colors.primary.background,
overflow: "hidden",
borderBottomWidth: 1,
borderBottomColor: hide ? "transparent" : colors.nav,
borderBottomColor: borderHidden
? "transparent"
: colors.secondary.background,
justifyContent: "space-between"
}
]}
>
{currentScreen === "Search" ? (
<SearchBar />
) : (
<>
<View style={styles.leftBtnContainer}>
<LeftMenus />
<Title />
</View>
<RightMenus />
</>
)}
<>
<View style={styles.leftBtnContainer}>
<LeftMenus
canGoBack={canGoBack}
onLeftButtonPress={onLeftMenuButtonPress}
/>
<Title
isHiddenOnRender={titleHiddenOnRender}
renderedInRoute={renderedInRoute}
id={id}
accentColor={accentColor}
title={title}
isBeta={isBeta}
/>
</View>
<RightMenus
renderedInRoute={renderedInRoute}
id={id}
headerRightButtons={headerRightButtons}
onPressDefaultRightButton={onPressDefaultRightButton}
search={hasSearch}
onSearch={onSearch}
/>
</>
</View>
</>
);
};
export const Header = React.memo(_Header, () => true);
const styles = StyleSheet.create({
container: {

View File

@@ -17,23 +17,29 @@ 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 from "react";
import { notesnook } from "../../../e2e/test.ids";
import { DDS } from "../../services/device-detection";
import Navigation from "../../services/navigation";
import useNavigationStore from "../../stores/use-navigation-store";
import { useSettingStore } from "../../stores/use-setting-store";
import { useThemeStore } from "../../stores/use-theme-store";
import { tabBarRef } from "../../utils/global-refs";
import { IconButton } from "../ui/icon-button";
export const LeftMenus = () => {
const colors = useThemeStore((state) => state.colors);
export const LeftMenus = ({
canGoBack,
onLeftButtonPress
}: {
canGoBack?: boolean;
onLeftButtonPress?: () => void;
}) => {
const { colors } = useThemeColors();
const deviceMode = useSettingStore((state) => state.deviceMode);
const canGoBack = useNavigationStore((state) => state.canGoBack);
const isTablet = deviceMode === "tablet";
const onLeftButtonPress = () => {
const _onLeftButtonPress = () => {
if (onLeftButtonPress) return onLeftButtonPress();
if (!canGoBack) {
if (tabBarRef.current?.isDrawerOpen()) {
Navigation.closeDrawer();
@@ -43,12 +49,6 @@ export const LeftMenus = () => {
return;
}
Navigation.goBack();
if (
useNavigationStore.getState().currentScreen.name === "Signup" ||
useNavigationStore.getState().currentScreen.name === "Login"
) {
tabBarRef.current.unlock();
}
};
return isTablet && !canGoBack ? null : (
@@ -60,21 +60,17 @@ export const LeftMenus = () => {
height: 40,
width: 40,
borderRadius: 100,
marginLeft: -5,
marginRight: DDS.isLargeTablet() ? 10 : 25
marginRight: DDS.isLargeTablet() ? 10 : 7
}}
left={40}
top={40}
right={DDS.isLargeTablet() ? 10 : 25}
onPress={onLeftButtonPress}
right={DDS.isLargeTablet() ? 10 : 10}
onPress={_onLeftButtonPress}
onLongPress={() => {
Navigation.popToTop();
}}
name={canGoBack ? "arrow-left" : "menu"}
color={colors.pri}
iconStyle={{
marginLeft: canGoBack ? -5 : 0
}}
color={colors.primary.paragraph}
/>
);
};

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