Compare commits

..

627 Commits

Author SHA1 Message Date
Ammar Ahmed
a4d115e88c mobile: export notes 2024-03-29 10:53:10 +05:00
Abdullah Atta
b277800a3b editor: remove display: inline for floating images 2024-03-18 08:19:48 +05:00
Abdullah Atta
43eef8d597 web: use new export utility from @notesnook/common 2024-03-18 08:19:28 +05:00
Abdullah Atta
14ea4fae3a web: expose streaming file decryption 2024-03-18 08:18:59 +05:00
Abdullah Atta
f5ae58350d web: allow reading data from stream when writing zip 2024-03-18 08:18:30 +05:00
Abdullah Atta
708aaf0ad0 common: add export notes utility
this utility resolves attachments & internal links
2024-03-18 08:16:45 +05:00
Abdullah Atta
182f63a22e core: minor changes 2024-03-18 08:15:42 +05:00
Abdullah Atta
7f120fcccd core: fix export of floating images 2024-03-18 08:15:25 +05:00
Abdullah Atta
14d82dd872 core: add support for resolving attachments & internal links 2024-03-18 08:15:00 +05:00
Abdullah Atta
8b22eab660 core: remove unused methods 2024-03-18 08:14:36 +05:00
Abdullah Atta
e23d4e9caf core: improve export performance 2024-03-18 08:14:08 +05:00
Ammar Ahmed
fd61dbfe32 mobile: fix publish button hidden on tablet 2024-03-15 11:04:04 +05:00
Ammar Ahmed
1a11bea05e mobile: cleanup 2024-03-15 10:54:27 +05:00
Ammar Ahmed
43e7c8c7f9 mobile: update deps 2024-03-15 10:51:36 +05:00
Ammar Ahmed
db90d4c443 mobile: fix app lock shows on session expired sheet 2024-03-15 10:50:53 +05:00
Ammar Ahmed
f375c7d3e4 mobile: support showing diff of conflicted note 2024-03-15 10:50:26 +05:00
Ammar Ahmed
fc5aeeb221 editor: add highlight diff extension 2024-03-15 10:49:37 +05:00
Ammar Ahmed
31685d12bd mobile: remove redundant code 2024-03-15 09:02:15 +05:00
Ammar Ahmed
5ffa11a7f6 mobile: render title after loaded 2024-03-15 08:51:24 +05:00
Ammar Ahmed
08e8035fcb mobile: bump android version 2024-03-15 08:08:16 +05:00
Ammar Ahmed
76b3c5ed7a mobile: fix button action 2024-03-15 08:07:11 +05:00
Ammar Ahmed
29468916e3 mobile: check if editorController.current is undefined 2024-03-15 08:05:22 +05:00
Ammar Ahmed
dde5dcd8fb mobile: cleanup 2024-03-15 07:44:33 +05:00
Ammar Ahmed
381184f8c4 mobile: add readonly editor 2024-03-15 07:43:25 +05:00
Abdullah Atta
5a78d9e517 web: make opfs safer by allowing only 1 operation at a time 2024-03-15 07:14:08 +05:00
Abdullah Atta
9edf5402c9 web: fix more tests 2024-03-14 13:07:01 +05:00
Abdullah Atta
74b49f24a8 editor: fix some images not loading 2024-03-14 13:07:01 +05:00
Abdullah Atta
dfcbf3045a web: fix lock/unlock from properties not working 2024-03-14 13:07:01 +05:00
Abdullah Atta
345922e74a web: fix title of locked note not saving 2024-03-14 13:07:01 +05:00
Abdullah Atta
21475c0514 web: update url on switching between sessions 2024-03-14 13:07:01 +05:00
Abdullah Atta
d498a7a2b9 web: hide tags input in new session 2024-03-14 13:07:01 +05:00
Abdullah Atta
e627c3326c web: bring back edited date in footer 2024-03-14 13:07:01 +05:00
Abdullah Atta
ad35e83269 web: fix typing fast in title input caused some characters to miss 2024-03-14 13:07:01 +05:00
Ammar Ahmed
38f04ca70a mobile: fix webpack config 2024-03-14 11:57:59 +05:00
Abdullah Atta
eeeda75408 global: update lockfiles 2024-03-14 11:11:52 +05:00
Abdullah Atta
5aec94f8a9 editor: fix type errors 2024-03-14 11:11:39 +05:00
Ammar Ahmed
ea274c5adf mobile: release v3.0.9 2024-03-14 10:56:12 +05:00
Abdullah Atta
81408f7763 web: show error if backup import fails 2024-03-14 10:53:23 +05:00
Ammar Ahmed
15dc333969 mobile: readd editor overlay 2024-03-14 10:44:55 +05:00
Ammar Ahmed
44a07c7cf6 mobile: fix tab bugs 2024-03-14 10:43:49 +05:00
Abdullah Atta
114338ab80 web: bump version to 3.0.7 2024-03-14 10:42:22 +05:00
Abdullah Atta
64e0129107 editor: do not add data-block-id attribute if no blockid 2024-03-14 10:37:28 +05:00
Abdullah Atta
749eea4979 editor: highlight active block for 5 seconds 2024-03-14 10:37:06 +05:00
Abdullah Atta
7ba785aa32 core: fix imports 2024-03-14 10:27:58 +05:00
Abdullah Atta
b4ec3fbbf1 web: improve internal links ui 2024-03-14 10:21:36 +05:00
Abdullah Atta
c0ff54afbb core: do not wrap text in content blocks 2024-03-14 10:13:19 +05:00
Abdullah Atta
6807871bb0 web: ignore realtime sync if it doesn't update content or note title 2024-03-14 10:04:00 +05:00
Abdullah Atta
6f07a3c03c web: fix behavior of editor action bar 2024-03-14 10:03:28 +05:00
Abdullah Atta
539c270c18 web: always show note statistics of active editor 2024-03-14 10:03:05 +05:00
Abdullah Atta
a7b190116a web: fix diff viewer not showing images 2024-03-14 10:02:42 +05:00
Abdullah Atta
ecba9f87be web: fix concurrent opfs reads throwing errors 2024-03-14 10:02:21 +05:00
Abdullah Atta
b11539d20f web: await promises 2024-03-14 10:01:47 +05:00
Abdullah Atta
66469b1fd5 web: use database updated event to add editor reactivity 2024-03-14 10:01:34 +05:00
Abdullah Atta
e098d65008 core: fix attachment not unlinked from notes on removal 2024-03-14 09:59:22 +05:00
Abdullah Atta
b837b9728e core: publish database updated events on content remove/update 2024-03-14 09:59:02 +05:00
Abdullah Atta
959f34c05c core: refactor database update event logic 2024-03-14 09:58:36 +05:00
Abdullah Atta
7465d4d149 core: add method to rebuild trash cache 2024-03-14 09:57:47 +05:00
Abdullah Atta
670e2bbd6d core: simplify remove from all notebooks logic 2024-03-14 09:57:02 +05:00
Abdullah Atta
7e4120e242 core: publish database updated event on relations unlink 2024-03-14 09:55:01 +05:00
Abdullah Atta
a76b26e64c core: fix note not unlinked from vault on unlock 2024-03-14 09:54:21 +05:00
Abdullah Atta
84d91d70d1 core: rebuild trash cache on sync complete 2024-03-14 09:54:00 +05:00
Abdullah Atta
cc2ba899e8 web: reopen notes after permanently deleting an attachment 2024-03-14 06:42:09 +05:00
Abdullah Atta
047efd5079 web: fix content is not updated for new notes in realtime sync 2024-03-14 06:41:39 +05:00
Abdullah Atta
3ae297ca04 web: update editor sessions in realtime 2024-03-14 06:41:17 +05:00
Abdullah Atta
82c7bcd0db web: do not show internal links, notebooks etc in properties for trash preview 2024-03-14 06:40:19 +05:00
Abdullah Atta
00e087023a web: fix editor title not changing on realtime sync 2024-03-14 06:39:39 +05:00
Abdullah Atta
c254f453ce web: special icons for readonly and trash preview tabs 2024-03-14 06:39:12 +05:00
Abdullah Atta
fb903f48e7 web: fix trash note preview is not readonly 2024-03-14 06:38:30 +05:00
Abdullah Atta
248d2a87b3 core: standarize database updated event 2024-03-13 11:01:50 +05:00
Abdullah Atta
268f5f012b global: upgrade typescript to 5.4.2 2024-03-13 09:01:12 +05:00
Abdullah Atta
ba0e8d5d3f web: fix type errors 2024-03-13 09:01:12 +05:00
Abdullah Atta
925cc7a3bc core: sort & group reminders in sqlite 2024-03-13 09:01:12 +05:00
Ammar Ahmed
e39e5a7bf0 mobile: minor cleanup 2024-03-13 08:59:57 +05:00
Ammar Ahmed
57de6fd014 mobile: fix search in share extension 2024-03-12 11:55:57 +05:00
Ammar Ahmed
679f6644f7 editor: do not focus editor on end search 2024-03-12 11:55:57 +05:00
Ammar Ahmed
b5e18fe350 mobile: fix editor tab bugs 2024-03-12 11:55:57 +05:00
Ammar Ahmed
9156c57b39 mobile: fix use-db-item hook not working 2024-03-12 11:55:57 +05:00
Ammar Ahmed
02bf88252c mobile: fix attachment exists check when uploading file 2024-03-12 11:55:57 +05:00
Ammar Ahmed
fe54930332 mobile: fix attachment progress not working 2024-03-12 11:55:57 +05:00
Ammar Ahmed
b7d72437de mobile: fix note with attachments not saved from share extension 2024-03-12 11:55:57 +05:00
Ammar Ahmed
2250299608 mobile: fix share extension append to exisitng note not working 2024-03-12 11:55:57 +05:00
Ammar Ahmed
1807109805 common: up version when migrating toolbar 2024-03-12 11:55:57 +05:00
Abdullah Atta
8b47626173 core: fix table x has no column y errors 2024-03-12 08:24:53 +05:00
Ammar Ahmed
ee9474afb7 mobile: blur editor on back 2024-03-12 08:24:53 +05:00
Abdullah Atta
a9b4c5e256 web: use dnd-kit for smoother drag/drop in tabs 2024-03-12 08:24:53 +05:00
Abdullah Atta
d41f049cee web: always sort pinned tabs first 2024-03-12 08:24:53 +05:00
Abdullah Atta
0e43408bff web: fix pinned tabs getting unpinned after app refresh 2024-03-12 08:24:53 +05:00
Ammar Ahmed
1c0a8e00a6 mobile: add useNoteLocked hook 2024-03-12 08:24:53 +05:00
Ammar Ahmed
c106db2422 mobile: fix ui of linked blocks 2024-03-12 08:24:53 +05:00
Ammar Ahmed
23734558dd mobile: fix jump to block 2024-03-12 08:24:53 +05:00
Ammar Ahmed
8ca8f54c33 mobile: fix content block ui 2024-03-12 08:24:53 +05:00
Ammar Ahmed
cad349ca12 mobile: fix loading placeholder position 2024-03-12 08:24:53 +05:00
Ammar Ahmed
ac7fb315d6 mobile: fix colors and size of icons in editor header 2024-03-12 08:24:53 +05:00
Ammar Ahmed
d7302a2cf0 mobile: use flatlist for rendering tabs 2024-03-12 08:24:53 +05:00
Ammar Ahmed
3254a1ff30 mobile: show keyboard when opening new tab or empty tab 2024-03-12 08:24:52 +05:00
Ammar Ahmed
2cab3dd486 mobile: make tab active on focus 2024-03-12 08:24:52 +05:00
Ammar Ahmed
e2767f2f77 mobile: fix tabs 2024-03-12 08:24:52 +05:00
Ammar Ahmed
4382576bf9 mobile: fix minor bugs in tabs 2024-03-12 08:24:52 +05:00
Ammar Ahmed
e89f7e3819 mobile: fix duplicate imports 2024-03-12 08:24:31 +05:00
Abdullah Atta
bced2b7872 web: bring back editor margins toggle in action bar 2024-03-12 08:24:31 +05:00
Abdullah Atta
7c6596f826 web: minor ui fixes 2024-03-12 08:24:31 +05:00
Abdullah Atta
1afad07b2d web: fix section subtitle horizontal margin 2024-03-12 08:24:31 +05:00
Abdullah Atta
43ccb398c0 web: add placeholder in table of contents 2024-03-12 08:24:30 +05:00
Abdullah Atta
5fbeb152e5 web: update diff content on tab switch 2024-03-12 08:24:30 +05:00
Abdullah Atta
d5e7fbabaa web: minor refactor 2024-03-12 08:24:30 +05:00
Abdullah Atta
33ff702295 web: fix properties section horizontal margin 2024-03-12 08:24:30 +05:00
Abdullah Atta
f3322cc8f1 core: remove note from vault if its content is not locked 2024-03-12 08:24:30 +05:00
Abdullah Atta
4356d10028 web: fix locked notes getting overwritten with unencrypted content 2024-03-12 08:24:30 +05:00
Abdullah Atta
371984a366 web: fix diff session not opening after app refresh 2024-03-12 08:24:30 +05:00
Abdullah Atta
e25016354e editor: fix search decorations not cleared on search end 2024-03-12 08:24:30 +05:00
Abdullah Atta
8fe4bddb85 web: add all missing functionality in note linking dialog 2024-03-12 08:24:30 +05:00
Abdullah Atta
fc121ef396 web: remove custom search state logic & use editor commands 2024-03-12 08:24:30 +05:00
Abdullah Atta
73a1295e29 editor: fix text on selected search result not visible 2024-03-12 08:24:30 +05:00
Abdullah Atta
8abf57ae24 editor: use global search store for managing search state 2024-03-12 08:24:30 +05:00
Abdullah Atta
a0f14dc3ef editor: minor refactor 2024-03-12 08:24:30 +05:00
Abdullah Atta
ce8cba4f13 web: remove locked session type guard (switch to simple boolean)
the type guard was incorrect
2024-03-12 08:24:30 +05:00
Abdullah Atta
e726bd073b web: add content to new session type 2024-03-12 08:24:30 +05:00
Abdullah Atta
3ee5ea097b web: fix editor title not updating on creating a note 2024-03-12 08:24:30 +05:00
Abdullah Atta
6272e8b805 web: remove preview session type 2024-03-12 08:24:30 +05:00
Abdullah Atta
ae5c323d3b web: ${id}_editorScroll > editorScroll_${id} 2024-03-12 08:24:29 +05:00
Abdullah Atta
afc3ede9b0 web: set session content before saving 2024-03-12 08:24:11 +05:00
Abdullah Atta
7671e0e297 web: add dialog container inside tab 2024-03-12 08:24:11 +05:00
Abdullah Atta
005f2d8ef1 web: handle locked notes properly in real time editor sync 2024-03-12 08:24:11 +05:00
Abdullah Atta
7b20acaa5a web: freeze unhydrated sessions 2024-03-12 08:24:11 +05:00
Abdullah Atta
da938ec348 web: remove unused & commented out code 2024-03-12 08:24:11 +05:00
Abdullah Atta
d32d438eeb web: focus editor on switching tab and toggle active class 2024-03-12 08:23:47 +05:00
Abdullah Atta
e289caf561 web: define editor & session once and pass it everywhere 2024-03-12 08:23:47 +05:00
Abdullah Atta
e8cd0c0a52 web: remove unlocked session type 2024-03-12 08:23:47 +05:00
Abdullah Atta
62bdee7789 web: set session id on saving note 2024-03-12 08:23:47 +05:00
Abdullah Atta
ccb9c492a8 core: add block ids before returning content 2024-03-12 08:23:46 +05:00
Abdullah Atta
4506e5e90a editor: fix range error crash 2024-03-12 08:23:46 +05:00
Abdullah Atta
2cc772106b editor: do not add block id on list items
(because they already contain paragraphs and we don't want duplicates)
2024-03-12 08:23:46 +05:00
Abdullah Atta
a82caf5da2 editor: do not touch editor when its view is destroyed 2024-03-12 08:23:46 +05:00
Abdullah Atta
1cebcb7876 editor: fix selection/focus is lost when editor state is updated 2024-03-12 08:23:46 +05:00
Abdullah Atta
0247903772 editor: fix list type thumbnails in toolbar 2024-03-12 08:23:46 +05:00
Abdullah Atta
1f5164660d editor: hide hover popup on editor destroy 2024-03-12 08:23:46 +05:00
Abdullah Atta
3a835ad90d editor: render interactive popups in popup instead of menu 2024-03-12 08:23:46 +05:00
Abdullah Atta
18c13ed8fe editor: scroll blocks into view only for active editor 2024-03-12 08:23:46 +05:00
Abdullah Atta
35345b9214 editor: query editor dom based on active editor class 2024-03-12 08:23:46 +05:00
Abdullah Atta
2c98b98765 editor: render all popups in popup container 2024-03-12 08:23:46 +05:00
Abdullah Atta
ace224b03c editor: close all popups and dropdowns on switching tabs 2024-03-12 08:23:46 +05:00
Abdullah Atta
23e9b07310 editor: fix placeholder is not visible 2024-03-12 08:23:46 +05:00
Abdullah Atta
fdc5642b3c ui: allow setting popup container 2024-03-12 08:23:46 +05:00
Abdullah Atta
9c81432dfd web: disable properties button in action bar when diff is open 2024-03-12 08:23:46 +05:00
Abdullah Atta
1825796649 web: allow session item size to grow 2024-03-12 08:23:46 +05:00
Abdullah Atta
9c5940e872 web: add support for /notes/:noteId/edit hash route 2024-03-12 08:23:46 +05:00
Abdullah Atta
ad77334e00 core: do not change date modified on items when bulk adding 2024-03-12 08:23:46 +05:00
Abdullah Atta
761b5b7793 editor: make image size clamping responsive 2024-03-12 08:23:45 +05:00
Abdullah Atta
eadf5260c4 web: fix race condition where editor context would be destroyed and updated at the same time 2024-03-12 08:23:45 +05:00
Abdullah Atta
bf62cd93b2 web: create error from ErrorEvent if none found 2024-03-12 08:23:45 +05:00
Abdullah Atta
92c9f31e01 editor: fix crash on loading web clip 2024-03-12 08:23:45 +05:00
Abdullah Atta
3404999bcb web: reenable tags refreshing in editor 2024-03-12 08:23:45 +05:00
Abdullah Atta
e9816ef3a2 editor: fix position of loading placeholder for floating image 2024-03-12 08:23:45 +05:00
Abdullah Atta
f10b7c9b66 editor: fix react node views not rendering on editor load 2024-03-12 08:23:45 +05:00
Abdullah Atta
36cf1fb950 web: fix crash on refreshing editor store 2024-03-12 08:23:45 +05:00
Ammar Ahmed
c1f4a4e3ab mobile: fix randId function duplicate 2024-03-12 08:23:45 +05:00
Abdullah Atta
50a22ddf85 editor: fix crash when resizing window due to a memory leak 2024-03-12 08:23:45 +05:00
Abdullah Atta
d3d74ff7df web: fix temporary tabs cannot be made permanent 2024-03-12 08:23:45 +05:00
Abdullah Atta
ed91e1e22f web: fix jumping tabs when pinning/unpinning tabs 2024-03-12 08:23:45 +05:00
Abdullah Atta
a765d22f4b web: fix crashes when moving tabs 2024-03-12 08:23:45 +05:00
Abdullah Atta
aca0e40fd9 web: fix new sessions can't be opened after app refresh 2024-03-12 08:23:45 +05:00
Abdullah Atta
183613296b web: fix type errors 2024-03-12 08:23:45 +05:00
Ammar Ahmed
3d75791006 editor: remove editor.current usage 2024-03-12 08:23:45 +05:00
Ammar Ahmed
516c214174 mobile: fix editor loading multiple times 2024-03-12 08:23:44 +05:00
Abdullah Atta
541868e681 web: use createInternalLink everywhere 2024-03-12 08:23:44 +05:00
Ammar Ahmed
c18513a04d mobile: finalize note linking 2024-03-12 08:23:44 +05:00
Ammar Ahmed
37220256fa core: parse internal link from href 2024-03-12 08:23:44 +05:00
Ammar Ahmed
e28a4b4e23 core: fix extracting internal links 2024-03-12 08:23:44 +05:00
Abdullah Atta
04d66576cb web: show references & linked notes in properties 2024-03-12 08:23:44 +05:00
Abdullah Atta
63ae8a4a86 web: reset active block after scroll 2024-03-12 08:23:44 +05:00
Abdullah Atta
6e0404fe8b core: add internal links extraction 2024-03-12 08:23:44 +05:00
Abdullah Atta
db79ad3f47 web: add full support for adding internal links 2024-03-12 08:23:44 +05:00
Abdullah Atta
46ac983770 editor: add support for adding/removing internal links 2024-03-12 08:23:44 +05:00
Abdullah Atta
03cc358e61 core: add support for processing internal links in note content 2024-03-12 08:23:43 +05:00
Ammar Ahmed
e457215248 mobile: note linking 2024-03-12 08:23:43 +05:00
Ammar Ahmed
bf393b8015 mobile: update deps 2024-03-12 08:21:28 +05:00
Abdullah Atta
b8eee77646 web: fix type errors and broken imports 2024-03-12 08:21:28 +05:00
Abdullah Atta
4ef4f8798c editor: reset editor on options update 2024-03-12 08:21:28 +05:00
Abdullah Atta
99886fdc96 web: add support for table of contents 2024-03-12 08:21:27 +05:00
Abdullah Atta
80ef5c890a web: add support for tabs 2024-03-12 08:21:03 +05:00
Abdullah Atta
c925ddd462 common: add utility for formatting history session time 2024-03-12 08:19:11 +05:00
Abdullah Atta
73d380d069 mobile: simplify unencrypted content check 2024-03-12 08:19:11 +05:00
Abdullah Atta
a67515118c core: simplify encryption content checks 2024-03-12 08:19:11 +05:00
Abdullah Atta
ad46f82337 core: restoring a history session should create a new version 2024-03-12 08:19:11 +05:00
Abdullah Atta
055a991fcb editor: create editor only once 2024-03-12 08:19:11 +05:00
Ammar Ahmed
d23c8203a3 mobile: implement toc 2024-03-12 08:19:11 +05:00
Ammar Ahmed
3b0ec278d4 mobile: support vault notes in tabs 2024-03-12 08:19:11 +05:00
Ammar Ahmed
55b4c77ad1 mobile: fix tab bugs 2024-03-12 08:19:11 +05:00
Ammar Ahmed
aa74541d6e mobile: add tab history 2024-03-12 08:19:11 +05:00
Ammar Ahmed
5c5e6c7438 mobile: some more fixes 2024-03-12 08:19:11 +05:00
Ammar Ahmed
a1235b48ef mobile: fix numerous issues with tabs 2024-03-12 08:19:10 +05:00
Ammar Ahmed
e015b180a8 mobile: push changes 2024-03-12 08:19:10 +05:00
Ammar Ahmed
e3c37c470d mobile: add editor tabs 2024-03-12 08:19:10 +05:00
Abdullah Atta
855203bd0e core: remove unnecessary notes collection init before migration 2024-03-12 08:19:10 +05:00
Abdullah Atta
6e4700121a web: bump version to 3.0.6 2024-03-12 08:19:10 +05:00
Abdullah Atta
a67b04db7b setup: only disable esbuild rebuild on mobile 2024-03-12 08:19:10 +05:00
Abdullah Atta
0f84d02abc setup: ignore esbuild rebuild script 2024-03-12 08:19:10 +05:00
Abdullah Atta
85c2be9500 mobile: update package lockfile 2024-03-12 08:19:10 +05:00
Abdullah Atta
a5a05f501a editor: fix task items not showing correctly 2024-03-12 08:19:10 +05:00
Abdullah Atta
9bb946d27e ui: fix menu is not scrollable on small screens 2024-03-12 08:19:10 +05:00
Abdullah Atta
cdd089fcf3 web: fix loading skeleton for compact items in list 2024-03-12 08:19:10 +05:00
Ammar Ahmed
49d4d2b6f9 mobile: bump version code 2024-03-12 08:19:09 +05:00
Ammar Ahmed
8d1408997f mobile: release v3.0.8 2024-03-12 08:19:09 +05:00
Ammar Ahmed
d18a8742f1 mobile: improve notebook sheet wording 2024-03-12 08:19:09 +05:00
Ammar Ahmed
d9d229b020 mobile: change sheet button positions 2024-03-12 08:19:09 +05:00
Ammar Ahmed
045527d502 mobile: cleanup 2024-03-12 08:19:09 +05:00
Ammar Ahmed
3c29303ac8 mobile: reminder notification shows even after removing reminders 2024-03-12 08:19:09 +05:00
Ammar Ahmed
98c6ee7904 mobile: simplify reminder sheet ui 2024-03-12 08:19:09 +05:00
Ammar Ahmed
a1dc396564 mobile: remove usage of customStyle prop 2024-03-12 08:19:09 +05:00
Abdullah Atta
4767597fc1 core: fix settings are not migrated 2024-03-12 08:19:09 +05:00
Ammar Ahmed
1e05512c98 mobile: update deps 2024-03-12 08:19:09 +05:00
Ammar Ahmed
68908e0e87 mobile: fix tags not showing properly 2024-03-12 08:19:09 +05:00
Ammar Ahmed
1808b84b0b mobile: cleanup 2024-03-12 08:19:09 +05:00
Ammar Ahmed
1a5595e3db mobile: update Pods 2024-03-12 08:19:08 +05:00
Ammar Ahmed
a96250b937 mobile: release v3.0.7 beta 2024-03-12 08:19:08 +05:00
Ammar Ahmed
19f4fbfcf7 mobile: fix sorting attachments 2024-03-12 08:19:08 +05:00
Abdullah Atta
35c8848b89 web: fix height of attachments table 2024-03-12 08:19:08 +05:00
Abdullah Atta
970f51595b core: do not include deleted attachments in filters 2024-03-12 08:19:08 +05:00
Abdullah Atta
b31d279e15 web: bump version to 3.0.5 2024-03-12 08:19:08 +05:00
Ammar Ahmed
f708eacdc3 mobile: fix mobile sheet ui 2024-03-12 08:19:08 +05:00
Abdullah Atta
7827a35de9 web: show error if there's no internet on updating profile 2024-03-12 08:19:08 +05:00
Abdullah Atta
5e0217bcb1 web: allow clearing database on database disk image is malformed 2024-03-12 08:19:08 +05:00
Abdullah Atta
54debb035a web: fix error due to changed file picker return 2024-03-12 08:19:08 +05:00
Abdullah Atta
3e42d3020d web: fix attachFile- when saving web clip 2024-03-12 08:19:08 +05:00
Ammar Ahmed
c7ab4c9ccf mobile: enable auto sync 2024-03-12 08:19:08 +05:00
Ammar Ahmed
d684c3a8b2 mobile: finalize image upload 2024-03-12 08:19:08 +05:00
Abdullah Atta
3feee27ad6 web: add image compression 2024-03-12 08:19:08 +05:00
Abdullah Atta
4add55adf2 web: add support for multiselecting attachments 2024-03-12 08:19:08 +05:00
ammarahm-ed
4ba90510ea mobile: handle uploading multiple files 2024-03-12 08:19:07 +05:00
ammarahm-ed
4b693b301f mobile: add optional image compression on upload 2024-03-12 08:19:07 +05:00
Abdullah Atta
a9fcd11e11 web: fix sync icon spin direction 2024-03-12 08:19:07 +05:00
Abdullah Atta
01ee348870 core: fix migration for db version > 6 2024-03-12 08:19:07 +05:00
Abdullah Atta
c0cec22e10 core: only get attachment id when processing content 2024-03-12 08:19:07 +05:00
Abdullah Atta
12b87cceeb web: enable auto sync 2024-03-12 08:19:07 +05:00
Abdullah Atta
b9cd40937d web: edit profile directly 2024-03-12 08:19:07 +05:00
Ammar Ahmed
1efae55a85 mobile: add user profile 2024-03-12 08:19:07 +05:00
Ammar Ahmed
932cf0c877 core: fetch user profile before updating profile 2024-03-12 08:19:07 +05:00
Abdullah Atta
0ba6be4fe6 web: add support for setting user profile 2024-03-12 08:19:06 +05:00
Abdullah Atta
cdaa3184a9 core: add support for setting user profile 2024-03-12 08:18:50 +05:00
Ammar Ahmed
03445bb45a mobile: cleanup 2024-03-12 08:18:50 +05:00
Ammar Ahmed
9d649c9bce mobile: fix note assigned to multiple colors 2024-03-12 08:18:50 +05:00
Ammar Ahmed
9a34e4884a mobile: small ui fixes 2024-03-12 08:18:50 +05:00
Ammar Ahmed
df0b51f5ec mobile: ui bug fixes 2024-03-12 08:18:50 +05:00
Ammar Ahmed
2554e5d63a mobile: user profile 2024-03-12 08:18:50 +05:00
Abdullah Atta
5479d800cf core: fix migration errors & compact deleted items 2024-03-12 08:17:21 +05:00
Ammar Ahmed
f729b8f908 mobile: release v3.0.6 2024-03-12 08:17:21 +05:00
Ammar Ahmed
af21da8f2e mobile: fix toolbar migration 2024-03-12 08:17:21 +05:00
Ammar Ahmed
9d13e9f700 core: check for deleted attachments in pending attachments 2024-03-12 08:17:21 +05:00
Ammar Ahmed
d9932a68d9 mobile: fix backup sheet never closes on manual backups 2024-03-12 08:17:21 +05:00
Ammar Ahmed
17a9d1851f mobile: allow sort by title in all groupings 2024-03-12 08:17:20 +05:00
Abdullah Atta
17f68b5315 desktop: disable os encryption of keystore in portable app 2024-03-12 08:16:11 +05:00
Abdullah Atta
b1ce214571 web: migrate toolbar on first use 2024-03-12 08:16:11 +05:00
Abdullah Atta
cfeba093d9 common: add toolbar migration 2024-03-12 08:16:11 +05:00
Abdullah Atta
69038d8edd global: update lockfiles 2024-03-12 08:16:10 +05:00
Abdullah Atta
934faf3b87 web: bump version to 3.0.4-beta 2024-03-12 08:16:01 +05:00
Abdullah Atta
71272d51c9 common: get note's locked status from its content 2024-03-12 08:16:01 +05:00
Abdullah Atta
b295e79677 core: always delete vault references on vault deletion 2024-03-12 08:16:01 +05:00
Abdullah Atta
74789675eb web: fix note not selected after switching from locked note 2024-03-12 08:16:01 +05:00
Abdullah Atta
1234d289eb core: fix sync tests 2024-03-12 08:16:01 +05:00
Abdullah Atta
723c29bad6 core: fix database image is malformed error when deleting vault 2024-03-12 08:16:01 +05:00
Abdullah Atta
343f1f320c core: fix database image is malformed error on unlocking a note 2024-03-12 08:16:01 +05:00
Abdullah Atta
50e70b90db web: fix sync not running after login 2024-03-12 08:16:01 +05:00
Abdullah Atta
a2fd5b266c web: refresh nav items on deleting a tag 2024-03-12 08:16:01 +05:00
Abdullah Atta
ebf32f0603 core: fix database image is malformed error 2024-03-12 08:16:01 +05:00
Abdullah Atta
4dae3a424c core: speed up backup restore 2024-03-12 08:16:01 +05:00
Ammar Ahmed
086aa53212 mobile: fix color of + button on hidden items
in side menu
2024-03-12 08:16:01 +05:00
Abdullah Atta
6d4b53b489 core: fix deleted items getting imported during backup restore 2024-03-12 08:16:00 +05:00
Abdullah Atta
c09064d2d6 core: fix duplicate colors created during backup restore 2024-03-12 08:16:00 +05:00
Ammar Ahmed
024be4ce89 mobile: fix readonly mode 2024-03-12 08:16:00 +05:00
Ammar Ahmed
513d6b3d72 editor: fix keyboard opens on drag in editor 2024-03-12 08:16:00 +05:00
Ammar Ahmed
513b4016b9 mobile: clear selection after deleting items 2024-03-12 08:16:00 +05:00
Ammar Ahmed
8de57a1b5d mobile: fix reminder ui in selection mode 2024-03-12 08:16:00 +05:00
Ammar Ahmed
34aba64bde mobile: remove pass min limit on vault 2024-03-12 08:16:00 +05:00
Abdullah Atta
eedfe38b26 web: clear editor if opened note is unlocked 2024-03-12 08:16:00 +05:00
Abdullah Atta
36406875fc core: remove note's vault relation regardless of vault 2024-03-12 08:15:43 +05:00
Abdullah Atta
854c2323e6 core: don't unlink vault notes on vault remove 2024-03-12 08:15:43 +05:00
Abdullah Atta
1c3694df9b web: fix vault settings not updating on deleting vault 2024-03-12 08:15:43 +05:00
Abdullah Atta
305b62b1a2 core: fix sqlite crash on calling delete with empty array 2024-03-12 08:15:43 +05:00
Abdullah Atta
5550a9f592 web: fix unnecessary rerendering of notebook header 2024-03-12 08:15:43 +05:00
Abdullah Atta
a97d917c7a web: disable auto sync 2024-03-12 08:15:42 +05:00
Abdullah Atta
a55e188ba8 core: enable sync push 2024-03-12 08:15:42 +05:00
Abdullah Atta
f0a70ac4da web: add drag/drop tests 2024-03-12 08:15:42 +05:00
Abdullah Atta
4d675a87a6 web: fix cannot navigate into nested notebook by hovering on it 2024-03-12 08:15:42 +05:00
Abdullah Atta
38c0a5a034 editor: fix react error 409 2024-03-12 08:15:42 +05:00
Abdullah Atta
607e901673 web: expand parent when a child notebook is added 2024-03-12 08:15:42 +05:00
Muhammad Ali
35b44c69b9 web: add support for item drag/droping (#3230)
* web: basic drag drop function for single note

* web: added check for noteId

* web: add support for item drag drop

---------

Co-authored-by: Abdullah Atta <abdullahatta@streetwriters.co>
2024-03-12 08:15:42 +05:00
Ammar Ahmed
d920c73acb mobile: disable auto sync 2024-03-12 08:15:42 +05:00
Ammar Ahmed
34f909e37f editor: fix callout margins 2024-03-12 08:15:42 +05:00
Ammar Ahmed
a33970250e mobile: fix keyboard jump on tap in dropdown 2024-03-12 08:15:42 +05:00
Abdullah Atta
297ca467d4 editor: fix rtl for outline list 2024-03-12 08:15:42 +05:00
Abdullah Atta
eeeb0908dd editor: add callout component 2024-03-12 08:15:42 +05:00
Ammar Ahmed
359a5ead1b mobile: release v3.0.5 beta 2024-03-12 08:15:42 +05:00
Ammar Ahmed
92fe455fc3 mobile: update toolbar 2024-03-12 08:15:41 +05:00
Abdullah Atta
31b2fe7e85 editor: add support for simple checklists 2024-03-12 08:15:41 +05:00
Ammar Ahmed
4b9f284ed9 editor: implement simple checklists 2024-03-12 08:15:25 +05:00
Abdullah Atta
85c812f271 web: show error if saving a note fails 2024-03-12 08:15:25 +05:00
Abdullah Atta
4aa4167351 core: fix database image is malformed error on updating locked note 2024-03-12 08:15:25 +05:00
Abdullah Atta
a8dfd91d12 web: bump version to 3.0.3-beta 2024-03-12 08:15:25 +05:00
Abdullah Atta
d23321aa68 desktop: include preload script when comparing bundles 2024-03-12 08:15:25 +05:00
Abdullah Atta
6c4ac1f2e7 core: add more lookup tests 2024-03-12 08:15:25 +05:00
Abdullah Atta
35e8b4bad6 core: allow testing with persisted database 2024-03-12 08:15:25 +05:00
Abdullah Atta
3285664e9f core: return items in same order when using .records 2024-03-12 08:15:24 +05:00
Abdullah Atta
7839bc2b6d core: improve notes lookup performance 2024-03-12 08:15:24 +05:00
Abdullah Atta
65f725dd00 core: refactor test setup & use better-sqlite-multiple-cipher 2024-03-12 08:15:24 +05:00
Abdullah Atta
b1f8b3d8f8 web: destroy sql database on app close 2024-03-12 08:15:24 +05:00
Abdullah Atta
83b6cbd98e core: fix database image is malformed error 2024-03-12 08:15:24 +05:00
Abdullah Atta
89737d9f90 core: fix note headling getting removed 2024-03-12 08:15:24 +05:00
Abdullah Atta
3b0e873c31 web: fix notebook indentation 2024-03-12 08:15:24 +05:00
Abdullah Atta
4bcd82fd7a core: fix sorting of newly created notes 2024-03-12 08:15:24 +05:00
Abdullah Atta
f5de8caa0d web: allow sorting by title everywhere 2024-03-12 08:15:24 +05:00
Abdullah Atta
13b821e061 core: add support for sorting by title & date together 2024-03-12 08:15:24 +05:00
Abdullah Atta
479f60518b web: add child notebook under opened notebook 2024-03-12 08:15:24 +05:00
Abdullah Atta
a71ac363bb web: rerender subnotebooks on root notebook change 2024-03-12 08:15:24 +05:00
Abdullah Atta
3b49a06d29 web: hide sort button in notebook view 2024-03-12 08:15:24 +05:00
Abdullah Atta
e443f3a8a3 web: await notes context refreshing 2024-03-12 08:15:24 +05:00
Abdullah Atta
6bb67318d8 web: clear keystore when clearing database 2024-03-12 08:15:24 +05:00
Abdullah Atta
0e79a5bfff web: allow clearing keystore 2024-03-12 08:15:24 +05:00
Abdullah Atta
20e7ff88b0 web: fix empty root notebook indentation 2024-03-12 08:15:24 +05:00
Abdullah Atta
e36f57850f web: fix monograph publish status not updating 2024-03-12 08:15:24 +05:00
Abdullah Atta
f76446440b web: fix nested notebook navigation from shortcuts 2024-03-12 08:15:23 +05:00
Abdullah Atta
873c3824e5 web: fix color cannot be renamed 2024-03-12 08:15:23 +05:00
Abdullah Atta
0db0b56be6 web: allow non-pro users to customize sidebar 2024-03-12 08:15:23 +05:00
Abdullah Atta
d3a7ec6b8c web: reimplement app lock & web key store 2024-03-12 08:15:23 +05:00
Ammar Ahmed
9d58dd9bab mobile: fix app lock on ios 2024-03-12 08:15:23 +05:00
Ammar Ahmed
409f5f1706 mobile: v3.0.4 2024-03-12 08:15:23 +05:00
Ammar Ahmed
1cd0c7b80a editor: fix color picker missing prop 2024-03-12 08:15:23 +05:00
Ammar Ahmed
bd6a2b2c48 mobile: improve verification flow before changing app lock 2024-03-12 08:15:23 +05:00
Ammar Ahmed
065a7fc683 mobile: fix reordering in side menu 2024-03-12 08:15:23 +05:00
Ammar Ahmed
adc55f719d editor: fix keyboard focus issues on mobile 2024-03-12 08:15:23 +05:00
Ammar Ahmed
4a266487b5 mobile: fix bugs 2024-03-12 08:15:23 +05:00
Ammar Ahmed
d7d5623cd6 mobile: fix sidemenu shortcuts 2024-03-12 08:15:22 +05:00
Abdullah Atta
5d21d3b621 web: remove security key config on app lock disable 2024-03-12 08:15:22 +05:00
Abdullah Atta
e622a379fd core: fix object id date parsing 2024-03-12 08:15:22 +05:00
Abdullah Atta
34acf8488e web: fix cr.isIndexedDBSupported is not a function 2024-03-12 08:15:22 +05:00
Ammar Ahmed
6d45a324db mobile: v3.0.3 beta 2024-03-12 08:15:22 +05:00
Ammar Ahmed
c8ccd31fe0 mobile: fix backup failed on iOS 2024-03-12 08:15:22 +05:00
Ammar Ahmed
6a2734acff mobile: bump version 2024-03-12 08:15:22 +05:00
Ammar Ahmed
e48aecc198 mobile: fix issue reporting 2024-03-12 08:15:22 +05:00
Ammar Ahmed
129dec46b3 mobile: fix rendering slow in list 2024-03-12 08:15:22 +05:00
Abdullah Atta
cbb360b714 ui: fix right clicking when menu is open doesn't close it 2024-03-12 08:15:22 +05:00
Abdullah Atta
eb9b67c0ca ui: fix hover highlighting 2 items in menu 2024-03-12 08:15:22 +05:00
Abdullah Atta
c71c3d834c web: fix color renaming and add color remove support 2024-03-12 08:15:22 +05:00
Abdullah Atta
3ccfad63bf web: add readonly indicator on notes 2024-03-12 08:15:22 +05:00
Abdullah Atta
806ce833cf web: fix compact view not affecting notes 2024-03-12 08:15:22 +05:00
Abdullah Atta
854a4d4f52 editor: fix Position -1 is outside of fragment error 2024-03-12 08:15:22 +05:00
Ammar Ahmed
9d16428781 mobile: fix migration bugs 2024-03-12 08:15:21 +05:00
Abdullah Atta
d3d4ab2f25 core: minor refactoring 2024-03-12 08:11:12 +05:00
Abdullah Atta
4575b0b516 core: migrate deleted items as well 2024-03-12 08:11:12 +05:00
Abdullah Atta
62226a186a web: refresh app on sync completed 2024-03-12 08:11:12 +05:00
Abdullah Atta
28e21acfc3 core: fix continuous invalid_grant error if login fails 2024-03-12 08:11:11 +05:00
Abdullah Atta
6d24a2e429 web: bump to v3.0.2-beta 2024-03-12 08:11:00 +05:00
Abdullah Atta
5a95ca5be8 web: hide window/tab title on app lock 2024-03-12 08:11:00 +05:00
Abdullah Atta
1ad1b91cc6 core: add support for creating backups of legacy collections 2024-03-12 08:11:00 +05:00
Abdullah Atta
a77a4f552d web: fix P.item is undefined error 2024-03-12 08:11:00 +05:00
Abdullah Atta
dade84ff7f core: remove deleteReason property 2024-03-12 08:11:00 +05:00
Ammar Ahmed
da5a7c7137 Update ios.publish.yml
Signed-off-by: Ammar Ahmed <40239442+ammarahm-ed@users.noreply.github.com>
2024-03-12 08:11:00 +05:00
Ammar Ahmed
e21d182cec mobile: cleanup gradle.properties 2024-03-12 08:11:00 +05:00
Ammar Ahmed
189f46cb3d mobile: fix share extension 2024-03-12 08:11:00 +05:00
Ammar Ahmed
ee98d87b11 mobile: beta release 2024-03-12 08:11:00 +05:00
Ammar Ahmed
d3a1aa0b0d mobile: fix notebook move to trash 2024-03-12 08:10:59 +05:00
Ammar Ahmed
b0e304bcea mobile: cleanup 2024-03-12 08:10:59 +05:00
Abdullah Atta
e84afd37a8 desktop: disable downgrades 2024-03-12 08:10:59 +05:00
Ammar Ahmed
64851302f4 mobile: stop app updates check in beta 2024-03-12 08:10:59 +05:00
Ammar Ahmed
dddbc671fb mobile: minor fixes 2024-03-12 08:10:59 +05:00
Abdullah Atta
5ea0254849 web: improve database deletion 2024-03-12 08:10:59 +05:00
Abdullah Atta
7ec9380fc9 web: reload page on reset from error boundary 2024-03-12 08:10:59 +05:00
Abdullah Atta
c9fb213fbb web: locked status not updating on note 2024-03-12 08:10:59 +05:00
Abdullah Atta
9a48f50a79 web: only save note's content through vault 2024-03-12 08:10:59 +05:00
Abdullah Atta
395dd8fb9e core: fix adding note to vault should return its id 2024-03-12 08:10:59 +05:00
Abdullah Atta
fcb88a9764 core: fix database resetting 2024-03-12 08:10:59 +05:00
Abdullah Atta
7d70cd2d3d theme: fix error input variant 2024-03-12 08:10:59 +05:00
Abdullah Atta
e5fbc61c26 web: allow typing custom date when adding reminder 2024-03-12 08:10:59 +05:00
Abdullah Atta
daf201cd6b common: use upcoming reminder when resolving reminders on note 2024-03-12 08:10:59 +05:00
Ammar Ahmed
6210dbc52a mobile: fix reminders 2024-03-12 08:10:59 +05:00
Ammar Ahmed
80f88f85ba mobile: fix notebooks & colors 2024-03-12 08:10:58 +05:00
Abdullah Atta
027e343841 core: set mode to "sync" for migrations during sync 2024-03-12 08:10:58 +05:00
Abdullah Atta
fc2f4ee57d desktop: patch better-sqlite3-multiple-ciphers to get prebuilt binaries 2024-03-12 08:10:58 +05:00
Abdullah Atta
0bd477ce9e web: bump to 3.0.1-beta 2024-03-12 08:10:58 +05:00
Abdullah Atta
145e778397 core: enable sync during tests 2024-03-12 08:10:58 +05:00
Abdullah Atta
0287257905 core: improve backup importing 2024-03-12 08:10:58 +05:00
Abdullah Atta
4b916dff47 core: add support for nested transactions 2024-03-12 08:10:58 +05:00
Abdullah Atta
7d18656033 web: use single transaction for importing a backup 2024-03-12 08:10:58 +05:00
Abdullah Atta
a935b677df web: make sqlite slightly faster 2024-03-12 08:09:52 +05:00
Ammar Ahmed
40ca5a3b4c mobile: fix release build 2024-03-12 08:09:52 +05:00
Abdullah Atta
4f9cd8dbb7 core: fix type errors 2024-03-12 08:09:51 +05:00
Ammar Ahmed
0d08318185 mobile: minor fixes 2024-03-12 08:09:51 +05:00
Ammar Ahmed
961dd2ef30 core: fix migration bugs 2024-03-12 08:09:21 +05:00
Ammar Ahmed
f7a5dbc71c mobile: fix saving user key 2024-03-12 08:09:21 +05:00
Abdullah Atta
303ac1db56 web: fix monograph publishing popup 2024-03-12 08:09:21 +05:00
Abdullah Atta
a421df8f1b web: ui fixes 2024-03-12 08:09:21 +05:00
Abdullah Atta
a8a9f56e02 web: fix app lock not getting disabled 2024-03-12 08:09:21 +05:00
Ammar Ahmed
236d17620d mobile: minor fixes 2024-03-12 08:09:21 +05:00
Ammar Ahmed
946b2a8555 mobile: locked notes 2024-03-12 08:09:21 +05:00
Abdullah Atta
6c868fb589 web: fix indexedDB not getting detected 2024-03-12 08:09:21 +05:00
Abdullah Atta
8bdd4b2638 core: destroy kysely on error 2024-03-12 08:09:21 +05:00
Abdullah Atta
d4deb90616 crypto: move libsodium & sodium-native to peer deps 2024-03-12 08:09:20 +05:00
Abdullah Atta
15744feb41 global: pin node version to 20 2024-03-12 08:09:20 +05:00
Abdullah Atta
e75005df89 web: various fixes 2024-03-12 08:09:20 +05:00
Abdullah Atta
b1fac8f9ce common: fix note resolution 2024-03-12 08:09:20 +05:00
Abdullah Atta
4ef77c5924 core: fix database migration 2024-03-12 08:09:20 +05:00
Abdullah Atta
9f1db293a0 core: disable sync push 2024-03-12 08:09:20 +05:00
Abdullah Atta
03ac543ae0 web: trigger error boundary on unhandled errors 2024-03-12 08:09:20 +05:00
Abdullah Atta
cf929ba1ce web: use safeStorage to encrypt secrets on desktop 2024-03-12 08:09:20 +05:00
Abdullah Atta
f3928db1ab desktop: use better-sqlite3 on desktop for SQLite 2024-03-12 08:09:20 +05:00
Abdullah Atta
e92dbadd8e desktop: fix autoupdater 2024-03-12 08:09:20 +05:00
Abdullah Atta
8eca2cb2b8 desktop: add api to delete files 2024-03-12 08:09:20 +05:00
Abdullah Atta
f0c5951eb0 desktop: expose electron's safeStorage api via trpc 2024-03-12 08:09:20 +05:00
Abdullah Atta
c3d18e4177 desktop: enable native modules rebuild during build 2024-03-12 08:09:20 +05:00
Ammar Ahmed
847037b2f7 core: return decrypted content on unlock note 2024-03-12 08:09:20 +05:00
Ammar Ahmed
6227e50dec mobile: applock with pin and password 2024-03-12 08:09:20 +05:00
Ammar Ahmed
89b6030bc3 mobile: minor fixes 2024-03-12 08:09:19 +05:00
Ammar Ahmed
248f692c2b mobile: sqlite-multiple-ciphers patch 2024-03-12 08:09:19 +05:00
Ammar Ahmed
6bac78117f mobile: refactor 2024-03-12 08:09:19 +05:00
Ammar Ahmed
e3ce44878e mobile: fix reorderable side menu 2024-03-12 08:09:19 +05:00
Ammar Ahmed
5f99783683 mobile: yearly reminders 2024-03-12 08:09:19 +05:00
Abdullah Atta
3d4cc1c4d7 web: add support for yearly reminders 2024-03-12 08:09:19 +05:00
Abdullah Atta
a2a05c5d8c core: add support for yearly reminders 2024-03-12 08:09:19 +05:00
Ammar Ahmed
c8eea0a0e4 mobile: stop bg fetch events when toggled off 2024-03-12 08:09:18 +05:00
Ammar Ahmed
8736a58a02 mobile: enable experimental background sync 2024-03-12 08:09:18 +05:00
Abdullah Atta
53c75ca6ef web: add support for hiding items from side bar 2024-03-12 08:09:18 +05:00
Abdullah Atta
506fc227c3 web: add support for reordering navigation menu items 2024-03-12 08:09:18 +05:00
Ammar Ahmed
4f40bdd262 mobile: cleanup 2024-03-12 08:09:18 +05:00
Ammar Ahmed
7bcd1df35a core: add sidebar order settings 2024-03-12 08:09:18 +05:00
Ammar Ahmed
1e4d63f84f mobile: reorder sidebar items 2024-03-12 08:09:18 +05:00
Abdullah Atta
7e7bb1b1b3 web: add support for custom colors 2024-03-12 08:09:18 +05:00
Ammar Ahmed
8e612db63f mobile: allow custom colors 2024-03-12 08:09:04 +05:00
Ammar Ahmed
140468cf3d mobile: migrate app lock 2024-03-12 08:09:04 +05:00
Abdullah Atta
114466e3a6 web: remove cbor-x package 2024-03-12 08:09:04 +05:00
Abdullah Atta
6c90653937 web: add support for at rest encryption and app lock 2024-03-12 08:09:03 +05:00
Ammar Ahmed
aec4c510f1 mobile: at rest encryption 2024-03-12 08:04:34 +05:00
Ammar Ahmed
8722157f33 mobile: simplify tests 2024-03-12 08:04:10 +05:00
Abdullah Atta
15b2aa6445 core: remove dummy test files 2024-03-12 08:04:10 +05:00
Abdullah Atta
7bddfa096f core: migrate key value storage to sqlite 2024-03-12 08:04:09 +05:00
Ammar Ahmed
7048c7e8db mobile: update sheet component 2024-03-12 08:03:42 +05:00
Abdullah Atta
508a259884 core: fix moving nested notebooks to trash 2024-03-12 08:03:42 +05:00
Abdullah Atta
8c5c9d447f global: move item resolution logic to @notesnook/common 2024-03-12 08:03:42 +05:00
Ammar Ahmed
fd951c3aaa mobile: add theme switch animation 2024-03-12 08:03:42 +05:00
Abdullah Atta
a026eb4a04 web: fix all type errors 2024-03-12 08:03:42 +05:00
Abdullah Atta
b02c43c99d web: fix duplicate imports 2024-03-12 08:03:12 +05:00
Abdullah Atta
c2be3a4f4d core: fix type error 2024-03-12 08:03:12 +05:00
Abdullah Atta
2b4155ad1d core: add tests for sync merger 2024-03-12 08:03:11 +05:00
Abdullah Atta
829a8a7ef8 core: migrate vault to a database collection 2024-03-12 08:03:11 +05:00
Ammar Ahmed
77c3e41564 mobile: fix exporter and attachment download 2024-03-12 08:03:11 +05:00
Ammar Ahmed
8e932b7ca0 global: fix type errors during build 2024-03-12 08:03:11 +05:00
Abdullah Atta
6e5c3e83d8 web: use simple transform stream for export progress 2024-03-12 08:03:11 +05:00
Abdullah Atta
a3db77285e web: fix exporting 2024-03-12 08:03:11 +05:00
Ammar Ahmed
0d864dfa10 mobile: add patches 2024-03-12 08:03:11 +05:00
Abdullah Atta
ae3d900af6 web: update export note snapshots for linux 2024-03-12 08:03:11 +05:00
Abdullah Atta
992a1d5231 web: fix reminder tests 2024-03-12 08:03:11 +05:00
Abdullah Atta
0a01ef6357 web: refresh notebooks after adding a new notebook 2024-03-12 08:03:11 +05:00
Abdullah Atta
7c6e79d78d web: fix force redirect to home on navigating to a color 2024-03-12 08:03:11 +05:00
Abdullah Atta
6084bb78a1 web: fix crash when sorting tags 2024-03-12 08:03:11 +05:00
Abdullah Atta
df987f6e94 web: fix crash when removing a notebook 2024-03-12 08:03:10 +05:00
Ammar Ahmed
a1e237a82a mobile: sync with latest core changes 2024-03-12 08:03:10 +05:00
Abdullah Atta
1e97e81fe7 core: remove unnecessary console.log 2024-03-12 08:03:10 +05:00
Ammar Ahmed
3ecda367f8 mobile: fix search to use filtered selector for notes 2024-03-12 08:03:10 +05:00
Abdullah Atta
afa4432521 core: prevent recursive sync after pushing items 2024-03-12 08:03:10 +05:00
Abdullah Atta
b24bc4fda0 web: simplify sync status 2024-03-12 08:03:10 +05:00
Abdullah Atta
0cca379a97 web: fix previewing trashed notes 2024-03-12 08:03:10 +05:00
Abdullah Atta
ca9e701386 core: fix trash 2024-03-12 08:03:10 +05:00
Abdullah Atta
48d79acbd3 web: improve sync test reliability 2024-03-12 08:03:10 +05:00
Abdullah Atta
2dabc62df3 core: auto sync not triggered on note update 2024-03-12 08:03:10 +05:00
Abdullah Atta
2ab7564622 web: fix note history tests 2024-03-12 08:01:37 +05:00
Abdullah Atta
0c0f19b23f web: fix selection logic in list 2024-03-12 08:01:37 +05:00
Abdullah Atta
1e5847e09e web: set testId on auth input fields 2024-03-12 08:01:37 +05:00
Abdullah Atta
e6d0061ca0 core: fix note content is not saving 2024-03-12 08:01:37 +05:00
Abdullah Atta
35b56fe8d2 core: fix data is not iterable error on backup restore 2024-03-12 08:01:37 +05:00
Abdullah Atta
4cd7f604df global: update lockfiles 2024-03-12 08:01:37 +05:00
Abdullah Atta
e65c36eff7 core: add retry if a test fails 2024-03-12 08:01:37 +05:00
Abdullah Atta
2e39e96846 core: fix invalid object-id generation for some timestamps 2024-03-12 08:01:37 +05:00
Abdullah Atta
357019a807 core: fix sync & monographs tests 2024-03-12 08:01:36 +05:00
Abdullah Atta
bb014cbb36 core: fix auto sync starting even when auto sync is disabled 2024-03-12 08:01:20 +05:00
Abdullah Atta
5d0e7ea28b core: fix sync tests 2024-03-12 08:00:59 +05:00
Abdullah Atta
96903c512b core: remove unnecessary migration success log 2024-03-12 08:00:59 +05:00
Abdullah Atta
b9acb8a213 core: fix Crypto is not supported on this platform. error during tests 2024-03-12 08:00:59 +05:00
Abdullah Atta
48e625480c core: make all the core tests pass again 2024-03-12 08:00:59 +05:00
Abdullah Atta
bc3dce6ecf web: fix crash when note has no title 2024-03-12 08:00:59 +05:00
Abdullah Atta
0da883b40a core: set default title if note has no title 2024-03-12 08:00:59 +05:00
Abdullah Atta
56d230191f web: update kysely patch 2024-03-12 08:00:59 +05:00
Abdullah Atta
abdb9626d0 web: run patch-package after npm i 2024-03-12 08:00:59 +05:00
Abdullah Atta
567eb789fc web: more fixes 2024-03-12 08:00:58 +05:00
Abdullah Atta
aa85b57e75 web: fix many type errors 2024-03-12 08:00:58 +05:00
Abdullah Atta
b5d9a83e1b global: update lockfiles 2024-03-12 07:58:39 +05:00
Abdullah Atta
ee2d0912fd theme: fix tinycolor2 not found error 2024-03-12 07:58:39 +05:00
Abdullah Atta
22cb2f1829 web: fix notes search in tags, favorites etc 2024-03-12 07:58:39 +05:00
Abdullah Atta
c3f76fa7c9 web: update wa-sqlite 2024-03-12 07:58:39 +05:00
Abdullah Atta
6923801426 web: use memory tempStore for sqlite 2024-03-12 07:58:39 +05:00
Abdullah Atta
210b6ba10c web: add support for exporting sql database 2024-03-12 07:58:39 +05:00
Abdullah Atta
b3bf5ff7a9 core: fix FTS5 SQLite search 2024-03-12 07:58:39 +05:00
Ammar Ahmed
ab1ff2f0bb mobile: cleanup 2024-03-12 07:58:38 +05:00
Ammar Ahmed
a813a5dd92 mobile: fix types 2024-03-12 07:58:38 +05:00
Ammar Ahmed
b85fc2d473 mobile: refactor zustand stores 2024-03-12 07:58:38 +05:00
Ammar Ahmed
112b44802f mobile: bug fixes 2024-03-12 07:58:38 +05:00
Ammar Ahmed
c121d4a010 core: add fn to get cache item 2024-03-12 07:58:07 +05:00
Abdullah Atta
26c7f04317 web: only auto align title & headline 2024-03-12 07:58:07 +05:00
Abdullah Atta
1fe3e5102b web: getKey -> key 2024-03-12 07:58:07 +05:00
Abdullah Atta
e0d44e58e6 web: fix resolved item data misaligned with items 2024-03-12 07:58:07 +05:00
Abdullah Atta
3103a7b97c core: fix many issues with virtualized grouping 2024-03-12 07:58:07 +05:00
Ammar Ahmed
ef802bfa5c core: early return when items are empty 2024-03-12 07:58:07 +05:00
Abdullah Atta
a1c2f3f679 web: fix random crashes when scrolling in list 2024-03-12 07:58:07 +05:00
Abdullah Atta
c7843effc6 web: replace react-virtual with react-virtuouso 2024-03-12 07:58:07 +05:00
Ammar Ahmed
1bd9ecca37 mobile: fix build ang bugs 2024-03-12 07:58:07 +05:00
Abdullah Atta
2f55f36802 web: hide attachments access if user is logged out 2024-03-12 07:58:07 +05:00
Abdullah Atta
af5b05df70 web: allow core to specify custom sql db name 2024-03-12 07:58:07 +05:00
Abdullah Atta
1b2479cad2 web: close idb connection on sqlite connection close 2024-03-12 07:58:07 +05:00
Abdullah Atta
b92b4bff4c core: clear database on logout 2024-03-12 07:58:06 +05:00
Ammar Ahmed
e8301907cd core: add tempStore option to DB 2024-03-12 07:58:06 +05:00
Abdullah Atta
2c3b400b5d core: migrate items sequentially during sync 2024-03-12 07:58:06 +05:00
Abdullah Atta
8350d24822 core: fix build error 2024-03-12 07:58:06 +05:00
Abdullah Atta
934f77d369 web: execute only 1 query at a time 2024-03-12 07:58:06 +05:00
Abdullah Atta
e8a9301434 web: fix diff viewer crashing the app 2024-03-12 07:58:06 +05:00
Abdullah Atta
58b51201d9 core: do not stop sync if a conflict occurs 2024-03-12 07:58:06 +05:00
Abdullah Atta
02bc3a9db4 core: allow only 1 transaction at a time 2024-03-12 07:58:06 +05:00
Abdullah Atta
b777546bc9 core: simplify item key derivation in virtualized grouping 2024-03-12 07:58:06 +05:00
Abdullah Atta
5b96b956e8 web: improve list item rendering performance 2024-03-12 07:58:06 +05:00
Abdullah Atta
e90f2d7087 core: always send realtime sync event 2024-03-12 07:58:06 +05:00
Abdullah Atta
8c1e0d2414 core: fix content date edited not updating 2024-03-12 07:58:06 +05:00
Abdullah Atta
007c9859dc core: put items in chunks of 200 2024-03-12 07:58:06 +05:00
Abdullah Atta
c72cac1213 web: set name prop on fields 2024-03-12 07:58:06 +05:00
Abdullah Atta
f8b6dd6d1a core: implement sync v2 2024-03-12 07:58:05 +05:00
Ammar Ahmed
5e71df03af mobile: use index based grouping 2024-03-12 07:55:47 +05:00
Abdullah Atta
e3f74f7f6f web: changes to use new grouping api 2024-03-12 07:55:47 +05:00
Abdullah Atta
5a759268b8 core: implement new high perf indexed based grouping 2024-03-12 07:55:47 +05:00
Ammar Ahmed
a9d98f73e4 mobile: fix share extension in release 2024-03-12 07:55:21 +05:00
Abdullah Atta
65dcd6e035 web: fix router types 2024-03-12 07:55:21 +05:00
Abdullah Atta
921b802aa0 web: enable vite hot reloading 2024-03-12 07:55:21 +05:00
Abdullah Atta
0b767de51b web: fix minor build issues 2024-03-12 07:55:21 +05:00
Abdullah Atta
e9bd553116 web: migrate everything to react@18 2024-03-12 07:55:21 +05:00
Abdullah Atta
d4fcf1687a web: fix notebook tests 2024-03-12 07:55:21 +05:00
Ammar Ahmed
996fb6d60f mobile: share extension cleanup 2024-03-12 07:55:21 +05:00
Ammar Ahmed
fd6d259d6c mobile: cleanup 2024-03-12 07:55:21 +05:00
Ammar Ahmed
4f16d172f8 mobile: migrate share extension 2024-03-12 07:55:21 +05:00
Ammar Ahmed
b94c3ac182 mobile: fix navigation 2024-03-12 07:55:21 +05:00
Abdullah Atta
ea383c80f2 web: send noteId when decrypting real-time locked content 2024-03-12 07:55:21 +05:00
Abdullah Atta
1b769f23cb web: fix tag tests 2024-03-12 07:55:20 +05:00
Ammar Ahmed
d5feab78c8 mobile: pass tests 2024-03-12 07:55:20 +05:00
Abdullah Atta
043099d217 web: fix html export snapshot 2024-03-12 07:55:20 +05:00
Abdullah Atta
670a8a05d1 core: fix md with frontmatter export 2024-03-12 07:55:20 +05:00
Ammar Ahmed
01ea478e46 core: fix ts error 2024-03-12 07:55:20 +05:00
Ammar Ahmed
ebd3ed06ab mobile: fix webpack build 2024-03-12 07:55:20 +05:00
Ammar Ahmed
f34591228e mobile: push changes 2024-03-12 07:55:20 +05:00
Abdullah Atta
7247ad4a05 web: fix notes tests 2024-03-12 07:55:20 +05:00
Abdullah Atta
e4fd25cb76 core: prevent duplicate colors based on color code 2024-03-12 07:55:00 +05:00
Abdullah Atta
3c67928583 core: fix error on locking notes 2024-03-12 07:55:00 +05:00
Ammar Ahmed
9aaa915caa mobile: push changes 2024-03-12 07:55:00 +05:00
Abdullah Atta
43318b185b web: reset search results on navigation 2024-03-12 07:55:00 +05:00
Abdullah Atta
015666388f web: reset prepared sql stmt on error 2024-03-12 07:55:00 +05:00
Abdullah Atta
542f7687f5 web: reuse search query 2024-03-12 07:55:00 +05:00
Abdullah Atta
5b744eae3c core: use porter trigram tokenizer for fts 2024-03-12 07:54:59 +05:00
Abdullah Atta
28cd11fdc7 core: properly escape fts query 2024-03-12 07:54:59 +05:00
Abdullah Atta
29ccbd0942 web: add support for assigning tags to notes 2024-03-12 07:54:59 +05:00
Abdullah Atta
30a127d2da core: fix "type" does not exist 2024-03-12 07:54:59 +05:00
Abdullah Atta
0d7973a34c core: fix build error 2024-03-12 07:54:59 +05:00
Ammar Ahmed
487edd7165 mobile: migrate to sqlite 2024-03-12 07:54:59 +05:00
Abdullah Atta
b3fe79b0a7 web: fix note editor properties 2024-03-12 07:53:23 +05:00
Abdullah Atta
966047585c common: type is now optional in getFormattedDate 2024-03-12 07:53:05 +05:00
Abdullah Atta
d57ca9b07c core: return FilteredSelector from attachments.ofNote 2024-03-12 07:53:05 +05:00
Abdullah Atta
e76b7ee0a5 web: show notebook & tag shortcuts when assigning notes 2024-03-12 07:53:05 +05:00
Abdullah Atta
2b42315c53 web: implement virtualized search for all items 2024-03-12 07:53:05 +05:00
Abdullah Atta
947443b4c7 web: use lazy loading to load attachments 2024-03-12 07:53:04 +05:00
Abdullah Atta
2547ab42e9 web: load tag & color title lazily 2024-03-12 07:52:42 +05:00
Abdullah Atta
306aca0645 web: show all nested notebooks as tree when moving notes 2024-03-12 07:52:42 +05:00
Abdullah Atta
f2b2dc33e6 web: add support for nested notebooks 2024-03-12 07:52:42 +05:00
Abdullah Atta
7a8f8dc565 web: use new virtualized grouping for reminders 2024-03-12 07:52:42 +05:00
Abdullah Atta
0528115ad3 web: use new virtualized grouping for trash 2024-03-12 07:52:42 +05:00
Abdullah Atta
4a5ee49db6 web: use new virtualized grouping for monographs 2024-03-12 07:52:42 +05:00
Abdullah Atta
3ca761013d core: add support for fetching trash as grouped 2024-03-12 07:52:42 +05:00
Abdullah Atta
bdeb123f18 web: install missing @hazae41/foras 2024-03-12 07:52:42 +05:00
Abdullah Atta
3f5f84aac0 core: fix attachment extraction for locked notes 2024-03-12 07:52:41 +05:00
Abdullah Atta
01d3bd7810 global: update lockfiles 2024-03-12 07:52:41 +05:00
Abdullah Atta
8e6d6e20d7 web: start initial migration to sqlite 2024-03-12 07:52:41 +05:00
Abdullah Atta
375185a9ea ui: add support for lazily loading menu items 2024-03-12 07:51:38 +05:00
Abdullah Atta
c2e9773486 common: remove total-notes utility 2024-03-12 07:51:38 +05:00
Abdullah Atta
5e0e7b17e2 core: changes according to client needs 2024-03-12 07:51:38 +05:00
Abdullah Atta
aa90684616 core: fix database migration 2024-03-12 07:51:37 +05:00
Abdullah Atta
fa47b4151f core: add support for incremental grouping 2024-03-12 07:51:24 +05:00
Abdullah Atta
eca5db19a2 core: fix size of nanoid in benchmarks 2024-03-12 07:51:24 +05:00
Abdullah Atta
b053b0eab4 core: optimize bson objectid generation & add benchmarks 2024-03-12 07:51:23 +05:00
Abdullah Atta
70da1045d7 core: fix monographs 2024-03-12 07:51:10 +05:00
Abdullah Atta
b5fd2b0751 core: fix sync tests 2024-03-12 07:51:10 +05:00
Abdullah Atta
97f6f09882 core: remove temporary sqlite db 2024-03-12 07:51:10 +05:00
Abdullah Atta
0c773727ee core: migrate everything to sqlite 2024-03-12 07:51:10 +05:00
Abdullah Atta
952ce39f1c core: migrate all database writes to sqlite 2024-03-12 07:49:55 +05:00
Abdullah Atta
057c7f3f5d global: update lockfiles 2024-03-12 07:49:14 +05:00
Abdullah Atta
9439c52801 core: convert everything else to typescript 2024-03-12 07:48:50 +05:00
Abdullah Atta
b1cebbe8f2 core: remove unnecessary sync tests 2024-03-12 07:48:50 +05:00
Abdullah Atta
836a47fd96 core: migrate monographs to typescript 2024-03-12 07:48:50 +05:00
Abdullah Atta
136b2ed457 core: make migration tests less flaky 2024-03-12 07:48:50 +05:00
Abdullah Atta
7f4006de95 core: add tests for migrations 2024-03-12 07:48:49 +05:00
Abdullah Atta
dc96209ed9 core: migrate settings to its own collection 2024-03-12 07:48:09 +05:00
Abdullah Atta
9b0332d5e0 core: get rid of topics; move to sub notebooks 2024-03-12 07:47:20 +05:00
Abdullah Atta
a1300fefd6 core: migrate attachments to use relations for linking to notes 2024-03-12 07:47:20 +05:00
Abdullah Atta
27032e853b core: minor updates and fixes 2024-03-12 07:47:20 +05:00
Abdullah Atta
22646e1398 core: fix types & syncing issues 2024-03-12 07:47:20 +05:00
ammarahm-ed
6e5e7c92c9 mobile: pass all tests 2024-03-12 07:41:34 +05:00
ammarahm-ed
3254109ea5 mobile: fix filesystem not loaded 2024-03-12 07:41:34 +05:00
ammarahm-ed
98ab2bfd8f mobile: fix navigating to tags 2024-03-12 07:41:34 +05:00
ammarahm-ed
6cbf22c163 mobile: migrate to typescript 2024-03-12 07:41:33 +05:00
ammarahm-ed
1714c1abca common: fix ts errors 2024-03-12 07:41:33 +05:00
Abdullah Atta
1bf65c7a53 mobile: use new types from core 2024-03-12 07:41:33 +05:00
Abdullah Atta
0075fde915 web: use new types from core 2024-03-12 07:41:33 +05:00
Abdullah Atta
318c61f531 core: fix more typings 2024-03-12 07:39:14 +05:00
Abdullah Atta
6dc502fd92 core: fix typing errors 2024-03-12 07:38:43 +05:00
Abdullah Atta
db7d7edcb5 core: convert almost everything to typescript 2024-03-12 07:38:43 +05:00
Ammar Ahmed
278840bcfd mobile: fix lint errors 2024-03-12 07:28:59 +05:00
Abdullah Atta
f149ad7c71 ci: update node version to 20 2024-03-12 07:28:59 +05:00
Abdullah Atta
ad29de8a04 config: update nx 2024-03-12 07:28:58 +05:00
Abdullah Atta
0759098414 desktop: set release channel to beta 2024-03-12 07:28:45 +05:00
Ammar Ahmed
528751a3ce mobile: update ios version number 2024-03-12 07:28:45 +05:00
Ammar Ahmed
3dd5bd8c4a ci: update workflow name
Signed-off-by: Ammar Ahmed <40239442+ammarahm-ed@users.noreply.github.com>
2024-03-12 07:28:23 +05:00
Ammar Ahmed
1ed84fe1b4 mobile: v3 beta release 2024-03-12 07:28:23 +05:00
Ammar Ahmed
1dcf8daecb ci: update iOS workflow
Signed-off-by: Ammar Ahmed <40239442+ammarahm-ed@users.noreply.github.com>
2024-03-12 07:25:38 +05:00
Ammar Ahmed
fc0ac1e02a Update android.publish.internal.yml
Signed-off-by: Ammar Ahmed <40239442+ammarahm-ed@users.noreply.github.com>
2024-03-12 07:25:38 +05:00
Ammar Ahmed
a1efab6673 ci: android.publish.yml
Signed-off-by: Ammar Ahmed <40239442+ammarahm-ed@users.noreply.github.com>
2024-03-12 07:25:38 +05:00
Abdullah Atta
4a6445e241 desktop: do not set teamId for notarization 2024-03-12 07:25:38 +05:00
Abdullah Atta
3fadb6016c ci: APPLE_API_KEY_ISSUER > APPLE_API_ISSUER 2024-03-12 07:25:38 +05:00
Abdullah Atta
99f469ba38 desktop: fix unknown property 'notarize' error 2024-03-12 07:25:38 +05:00
Abdullah Atta
d1753f449a desktop: use official electron builder notarization 2024-03-12 07:25:38 +05:00
Abdullah Atta
fe19a6b0cc ci: update {upload,download}-artifact to v4 2024-03-12 07:25:38 +05:00
Abdullah Atta
bb67472dee ci: get rid of you may only define one of branches and branches-ignore for a single event 2024-03-12 07:25:38 +05:00
Abdullah Atta
13213cf4cd ci: use m1 for building on mac 2024-03-12 07:25:23 +05:00
Abdullah Atta
b49a890849 ci: use wrangler directly to publish to cf 2024-03-12 07:25:23 +05:00
Abdullah Atta
3eb21afd42 desktop: use app version from autoUpdater 2024-03-12 07:25:23 +05:00
Abdullah Atta
b98021929f ci: update cloudflare action 2024-03-12 07:25:23 +05:00
Abdullah Atta
40ccad1181 desktop: ready beta for deployment 2024-03-12 07:25:23 +05:00
Abdullah Atta
e2db62d551 ci: deploy to correct cf pages project
Signed-off-by: Abdullah Atta <abdullahatta@streetwriters.co>
2024-03-12 07:24:45 +05:00
Abdullah Atta
0229641917 core: turn off sync push by default 2024-03-12 07:24:45 +05:00
1225 changed files with 103861 additions and 131908 deletions

View File

@@ -15,7 +15,6 @@ const SCOPES = [
"mobile",
"web",
"vericrypt",
"monograph",
"desktop",
"crypto",
"editor",

View File

@@ -147,10 +147,8 @@ jobs:
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 sourcemaps
uses: actions/upload-artifact@v4
- name: Upload signed aab to Github
uses: actions/upload-artifact@v2
with:
name: sourcemaps
path: |
apps/mobile/native/android/app/build/**/*.map
packages/editor-mobile/sourcemaps/*.map
name: Notesnook.aab
path: ${{steps.sign_app.outputs.signedReleaseFile}}

View File

@@ -22,7 +22,7 @@ jobs:
- name: Use specific Java version for the builds
uses: joschi/setup-jdk@v2
with:
java-version: "17"
java-version: "11"
architecture: "x64"
- name: Install node modules
@@ -133,6 +133,7 @@ jobs:
status: completed
whatsNewDirectory: apps/mobile/native/android/releasenotes/
- name: Create release draft on Github
uses: softprops/action-gh-release@v1
with:
@@ -147,10 +148,8 @@ jobs:
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 sourcemaps
uses: actions/upload-artifact@v4
- name: Upload signed aab to Github
uses: actions/upload-artifact@v2
with:
name: sourcemaps
path: |
apps/mobile/native/android/app/build/**/*.map
packages/editor-mobile/sourcemaps/*.map
name: Notesnook.aab
path: ${{steps.sign_app.outputs.signedReleaseFile}}

View File

@@ -97,9 +97,6 @@ jobs:
- name: Setup Node
uses: ./.github/actions/setup-node-with-cache
- name: Install setuptools
run: brew install python-setuptools
- name: Setup notarization
run: |
mkdir -p ~/private_keys/
@@ -147,7 +144,7 @@ jobs:
CSC_KEY_PASSWORD: ${{ secrets.mac_certs_password }}
run: |
npx nx run release --project @notesnook/desktop -- --variant=mas
yarn electron-builder --mac mas --universal -p never
npx electron-builder --mac mas --universal -p never
working-directory: ./apps/desktop
- name: Build zip and dmg
@@ -161,9 +158,9 @@ jobs:
run: |
npx nx run release --project @notesnook/desktop
if [ ${{ inputs.publish-github }} == true ]; then
yarn electron-builder --mac zip dmg --arm64 --x64 -p always
npx electron-builder --mac zip dmg --arm64 --x64 -p always
else
yarn electron-builder --mac zip dmg --arm64 --x64 -p never
npx electron-builder --mac zip dmg --arm64 --x64 -p never
fi
working-directory: ./apps/desktop
@@ -219,7 +216,7 @@ jobs:
- name: Build snap
if: inputs.publish-snap
run: |
yarn electron-builder --linux snap:x64 -p never
npx electron-builder --linux snap:x64 -p never
working-directory: ./apps/desktop
- name: Build AppImage
@@ -227,9 +224,9 @@ jobs:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
if [ ${{ inputs.publish-github }} == true ]; then
yarn electron-builder --linux AppImage:x64 AppImage:arm64 -p always
npx electron-builder --linux AppImage:x64 AppImage:arm64 -p always
else
yarn electron-builder --linux AppImage:x64 AppImage:arm64 -p never
npx electron-builder --linux AppImage:x64 AppImage:arm64 -p never
fi
working-directory: ./apps/desktop
@@ -254,8 +251,8 @@ jobs:
with:
dotnet-version: 6.0.x
- name: Install TrustedSigning
run: Install-Module -Name TrustedSigning -RequiredVersion 0.3.15 -Force -Repository PSGallery
- name: Install AzureCodeSigning
run: Install-Module -Name AzureCodeSigning -RequiredVersion 0.3.0 -Force -Repository PSGallery
shell: pwsh
- name: Setup Node

View File

@@ -98,10 +98,7 @@ jobs:
api-private-key: ${{ secrets.API_KEY }}
- name: Upload Notesnook.ipa to Github
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v2
with:
name: Notesnook.zip
path: |
Notesnook.ipa
apps/mobile/native/ios/**/*.map
packages/editor-mobile/sourcemaps/*.map
name: Notesnook.ipa
path: Notesnook.ipa

View File

@@ -1,83 +0,0 @@
# This workflow uses actions that are not certified by GitHub.
# They are provided by a third-party and are governed by
# separate terms of service, privacy policy, and support
# documentation.
# GitHub recommends pinning actions to a commit SHA.
# To get a newer version, you will need to update the SHA.
# You can also reference a tag or branch, but the action may change without warning.
name: Publish @notesnook/monograph
on: workflow_dispatch
jobs:
push_to_registry:
name: Push Docker image to Docker Hub
runs-on: ubuntu-latest
permissions:
packages: write
contents: read
attestations: write
id-token: write
steps:
- name: Check out the repo
uses: actions/checkout@v4
- 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=monograph
- name: Generate build
run: npx nx build @notesnook/monograph
# Setup Buildx
- name: Docker Setup Buildx
uses: docker/setup-buildx-action@v3
with:
platforms: linux/amd64,linux/arm64,linux/arm/v7,linux/arm/v8
- name: Log in to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
ecr: auto
logout: true
# Pull previous image from docker hub to use it as cache to improve the image build time.
- name: docker pull cache image
continue-on-error: true
run: docker pull streetwriters/monograph:latest
# Setup QEMU
- name: Set up QEMU
uses: docker/setup-qemu-action@v2
- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@v5
with:
images: streetwriters/monograph
- name: Build and push Docker image
id: push
uses: docker/build-push-action@v6
with:
context: apps/monograph
file: apps/monograph/Dockerfile
push: true
platforms: linux/amd64,linux/arm64,linux/arm/v7,linux/arm/v8
tags: ${{ steps.meta.outputs.tags }}
cache-from: streetwriters/monograph:latest
- name: Generate artifact attestation
uses: actions/attest-build-provenance@v1
with:
subject-name: index.docker.io/streetwriters/monograph
subject-digest: ${{ steps.push.outputs.digest }}
push-to-registry: true

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

@@ -40,7 +40,7 @@ jobs:
run: npm run build:test:web
- name: Archive build artifact
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v2
with:
name: build
path: apps/web/build/**/*
@@ -56,7 +56,7 @@ jobs:
uses: actions/checkout@v3
- name: Download build
uses: actions/download-artifact@v4
uses: actions/download-artifact@v3
with:
name: build
path: ./apps/web/build
@@ -67,7 +67,7 @@ jobs:
- name: Install packages
run: |
npm ci --ignore-scripts --prefer-offline --no-audit
npm install -D @playwright/test otplib dayjs @zip.js/zip.js
npm install -D @playwright/test otplib
- name: Validate build extraction
run: ls -ld ./apps/web/build/index.html
@@ -88,7 +88,7 @@ jobs:
working-directory: apps/web
- name: Upload test results
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v3
if: failure()
with:
name: test-results-${{ matrix.shard }}

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/

View File

@@ -40,35 +40,12 @@ Once you are inside the `./notesnook` directory, run the preparation step:
npm install
```
Now you can finally start the desktop app for development:
Now you can finally start the desktop app:
```bash
npm run start:desktop
```
To run the app in release mode:
```bash
npm run staging -- --rebuild
```
This will compile and run the app in production mode but it won't generate any packages. To create the final packages, you'll have to run the following commands:
```bash
npm run release -- --rebuild
# For macOS
npx electron-builder --mac dmg --arm64 --x64 -publish never
# For Linux (AppImage)
npx electron-builder --linux AppImage:x64 AppImage:arm64 -publish never
# For Windows
npx electron-builder --win --publish never
```
Feel free to play around with the `electron-builder` command to get the packages you need. `npx electron-builder --help` is a great resource to learn different commands & platforms supported by `electron-builder`.
## Developer guide
### The tech stack

View File

@@ -1,4 +1,3 @@
owner: streetwriters
repo: notesnook
provider: github
updaterCacheDirName: Notesnook

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

After

Width:  |  Height:  |  Size: 118 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 401 B

After

Width:  |  Height:  |  Size: 520 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 543 B

After

Width:  |  Height:  |  Size: 917 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.3 KiB

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 763 B

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 353 KiB

After

Width:  |  Height:  |  Size: 353 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.3 KiB

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.3 KiB

After

Width:  |  Height:  |  Size: 36 KiB

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": "3.0.18",
"version": "3.0.7-beta",
"appAppleId": "1544027013",
"private": true,
"main": "./dist/index.js",
@@ -11,29 +11,27 @@
"repository": "https://github.com/streetwriters/notesnook",
"license": "GPL-3.0-or-later",
"dependencies": {
"@trpc/client": "10.45.2",
"@trpc/server": "10.45.2",
"better-sqlite3-multiple-ciphers": "11.2.1",
"electron-trpc": "0.6.1",
"electron-updater": "^6.3.4",
"icojs": "^0.19.4",
"@notesnook/crypto": "file:../../packages/crypto",
"@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.4",
"icojs": "^0.17.1",
"sodium-native": "^4.0.6",
"typed-emitter": "^2.1.0",
"yargs": "^17.7.2",
"zod": "^3.23.8"
"yargs": "^17.6.2",
"zod": "^3.21.4"
},
"devDependencies": {
"@streetwriters/kysely": "^0.27.4",
"@types/node": "22.7.0",
"@types/yargs": "^17.0.33",
"chokidar": "^4.0.1",
"electron": "^30.5.1",
"electron-builder": "^25.0.5",
"esbuild": "^0.24.0",
"node-abi": "^3.68.0",
"node-gyp-build": "^4.8.2",
"prebuildify": "^6.0.1",
"@types/node": "18.16.1",
"@types/yargs": "^17.0.24",
"chokidar": "^3.5.3",
"electron": "^28.2.1",
"electron-builder": "^24.9.1",
"esbuild": "^0.20.0",
"tree-kill": "^1.2.2",
"undici": "^6.19.8"
"undici": "^6.6.1"
},
"optionalDependencies": {
"dmg-license": "^1.0.11"
@@ -57,8 +55,8 @@
"productName": "Notesnook",
"copyright": "Copyright © 2023 Streetwriters (Private) Limited",
"artifactName": "notesnook_${os}_${arch}.${ext}",
"generateUpdatesFilesForAllChannels": true,
"asar": false,
"generateUpdatesFilesForAllChannels": true,
"files": [
"!*.chunk.js.map",
"!*.chunk.js.LICENSE.txt",
@@ -158,8 +156,7 @@
},
"nsis": {
"oneClick": true,
"createDesktopShortcut": "always",
"deleteAppDataOnUninstall": true
"createDesktopShortcut": "always"
},
"linux": {
"target": [
@@ -222,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 739a48c..7a7b4af 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
@@ -117,6 +117,7 @@ class LinuxTargetHelper {
@@ -116,6 +116,7 @@ class LinuxTargetHelper {
StartupWMClass: appInfo.productName,
...extra,
...targetSpecificOptions.desktop,
@@ -10,7 +10,7 @@ index 739a48c..7a7b4af 100644
};
const description = this.getDescription(targetSpecificOptions);
if (!(0, builder_util_1.isEmptyOrSpaces)(description)) {
@@ -160,6 +161,23 @@ class LinuxTargetHelper {
@@ -159,6 +160,23 @@ class LinuxTargetHelper {
data += `\n${name}=${desktopMeta[name]}`;
}
data += "\n";

View File

@@ -0,0 +1,23 @@
diff --git a/node_modules/electron-trpc/dist/main.mjs b/node_modules/electron-trpc/dist/main.mjs
index 379cf3b..2644e5d 100644
--- a/node_modules/electron-trpc/dist/main.mjs
+++ b/node_modules/electron-trpc/dist/main.mjs
@@ -221,9 +221,16 @@ class G {
i(this, c).includes(r) || (i(this, c).push(r), I(this, T, W).call(this, r));
}
detachWindow(r) {
+
y(this, c, i(this, c).filter((n) => n !== r));
- for (const [n, t] of i(this, u).entries())
- n.startsWith(`${r.webContents.id}-`) && (t.unsubscribe(), i(this, u).delete(n));
+ for (const [n, t] of i(this, u).entries()) {
+ try {
+ n.startsWith(`${r.webContents.id}-`) && (t.unsubscribe(), i(this, u).delete(n));
+ } catch(e) {
+ console.error(e);
+ // ignore
+ }
+ }
}
}
c = new WeakMap(), u = new WeakMap(), T = new WeakSet(), W = function(r) {

View File

@@ -1,39 +0,0 @@
diff --git a/node_modules/electron-trpc/dist/main.mjs b/node_modules/electron-trpc/dist/main.mjs
index 07971db..b67a3cf 100644
--- a/node_modules/electron-trpc/dist/main.mjs
+++ b/node_modules/electron-trpc/dist/main.mjs
@@ -537,8 +537,15 @@ class be {
attachWindow(r) {
F(this, w).includes(r) || (y("Attaching window", r.id), F(this, w).push(r), v(this, N, Z).call(this, r));
}
- detachWindow(r) {
- y("Detaching window", r.id), L(this, w, F(this, w).filter((e) => e !== r)), v(this, R, M).call(this, { webContentsId: r.webContents.id });
+ detachWindow(r, webContentsId) {
+ y("Detaching window", r.id);
+
+ if (r.isDestroyed() && webContentsId === undefined) {
+ throw new Error('webContentsId is required when calling detachWindow on a destroyed window');
+ }
+
+ L(this, w, F(this, w).filter((e) => e !== r));
+ v(this, R, M).call(this, { webContentsId: webContentsId ?? r.webContents.id });
}
}
w = new WeakMap(), E = new WeakMap(), R = new WeakSet(), M = function({
@@ -548,6 +555,7 @@ w = new WeakMap(), E = new WeakMap(), R = new WeakSet(), M = function({
for (const [i, a] of F(this, E).entries())
i.startsWith(`${r}-${e ?? ""}`) && (y("Closing subscription", i), a.unsubscribe(), F(this, E).delete(i));
}, N = new WeakSet(), Z = function(r) {
+ const webContentsId = r.webContents.id;
r.webContents.on("did-start-navigation", ({ frame: e }) => {
y(
"Handling webContents `did-start-navigation` event",
@@ -558,7 +566,7 @@ w = new WeakMap(), E = new WeakMap(), R = new WeakSet(), M = function({
frameRoutingId: e.routingId
});
}), r.webContents.on("destroyed", () => {
- y("Handling webContents `destroyed` event"), this.detachWindow(r);
+ y("Handling webContents `destroyed` event"), this.detachWindow(r, webContentsId);
});
};
const me = ({

View File

@@ -19,7 +19,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
import path from "path";
import fs, { readFile, writeFile } from "fs/promises";
import { existsSync, readFileSync } from "fs";
import { existsSync } from "fs";
import yargs from "yargs-parser";
import os from "os";
import * as childProcess from "child_process";
@@ -28,29 +28,23 @@ import { fileURLToPath } from "url";
const args = yargs(process.argv);
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const packageJson = JSON.parse(
readFileSync(path.join(__dirname, "..", "package.json"), "utf-8")
);
const webAppPath = path.resolve(path.join(__dirname, "..", "..", "web"));
await fs.rm("./build/", { force: true, recursive: true });
if (args.rebuild || !existsSync(path.join(webAppPath, "build"))) {
await exec(
"yarn nx build:desktop @notesnook/web",
path.join(__dirname, "..", "..", "..")
);
await exec(`cd ${webAppPath} && npm run build:desktop`);
}
// temporary until there's support for prebuilt binaries for linux ARM
if (os.platform() === "linux") await patchBetterSQLite3();
// 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 -t electron@${packageJson.devDependencies.electron}`,
// path.join(__dirname, "..", "node_modules", "sodium-native")
// );
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,
@@ -58,19 +52,19 @@ await fs.cp(path.join(webAppPath, "build"), "build", {
});
if (args.variant === "mas") {
await exec(`yarn run bundle:mas`);
await exec(`npm run bundle:mas`);
} else {
await exec(`yarn run bundle`);
await exec(`npm run bundle`);
}
await exec(`yarn tsc`);
await exec(`npx tsc`);
if (args.run) {
await exec(`yarn electron-builder --dir --x64`);
await exec(`npx electron-builder --dir --x64`);
if (process.platform === "win32") {
await exec(`.\\output\\win-unpacked\\Notesnook.exe`);
} else if (process.platform === "darwin") {
await exec(`./output/mac/Notesnook.app/Contents/MacOS/Notesnook`);
await exec(`./output/darwin-unpacked/Notesnook`);
} else {
await exec(`./output/linux-unpacked/Notesnook`);
}
@@ -94,7 +88,7 @@ async function patchBetterSQLite3() {
);
const json = JSON.parse(await readFile(jsonPath, "utf-8"));
json.version = "11.2.2";
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";

View File

@@ -53,11 +53,11 @@ async function onChange(first) {
if (first) {
await fs.rm("./build/", { force: true, recursive: true });
await exec("yarn electron-builder install-app-deps");
await exec("npx electron-rebuild");
}
await exec(`yarn run bundle`);
execAsync(`yarn`, [`tsc`]);
await exec(`npm run bundle`);
execAsync(`npx`, [`tsc`]);
if (await isBundleSame()) {
console.log("Bundle is same. Doing nothing.");
@@ -66,7 +66,7 @@ async function onChange(first) {
if (first) {
await spawnAndWaitUntil(
["yarn", "nx", "start:desktop", "@notesnook/web"],
["npx", "nx", "start:desktop", "@notesnook/web"],
path.join(__dirname, "..", "..", ".."),
(data) => data.includes("Network: use --host to expose")
);
@@ -78,7 +78,7 @@ async function onChange(first) {
}
execAsync(
"yarn",
"npx",
["electron", path.join("build", "electron.js")],
true,
cleanup

View File

@@ -33,7 +33,7 @@ module.exports = async function (configuration) {
const DescriptionUrl = "https://notesnook.com/";
const FilesCatalog = createCatalog(configuration.path);
const command = `Invoke-TrustedSigning -Endpoint "${Endpoint}" -CodeSigningAccountName "${CodeSigningAccountName}" -CertificateProfileName "${CertificateProfileName}" -FileDigest "${FileDigest}" -TimestampRfc3161 "${TimestampRfc3161}" -TimestampDigest "${TimestampDigest}" -Description "${Description}" -DescriptionUrl "${DescriptionUrl}" -FilesCatalog "${FilesCatalog}"`;
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);

View File

@@ -24,7 +24,6 @@ import { spellCheckerRouter } from "./spell-checker";
import { updaterRouter } from "./updater";
import { bridgeRouter } from "./bridge";
import { safeStorageRouter } from "./safe-storage";
import { windowRouter } from "./window";
const t = initTRPC.create();
@@ -34,8 +33,7 @@ export const router = t.router({
spellChecker: spellCheckerRouter,
updater: updaterRouter,
bridge: bridgeRouter,
safeStorage: safeStorageRouter,
window: windowRouter
safeStorage: safeStorageRouter
});
export const api = router.createCaller({});

View File

@@ -19,7 +19,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
import { initTRPC } from "@trpc/server";
import { z } from "zod";
import { app, dialog, nativeTheme, Notification, shell } from "electron";
import { dialog, nativeTheme, Notification, shell } from "electron";
import { AutoLaunch } from "../utils/autolaunch";
import { config, DesktopIntegration } from "../utils/config";
import { bringToFront } from "../utils/bring-to-front";
@@ -32,7 +32,6 @@ import { AssetManager } from "../utils/asset-manager";
import { isFlatpak } from "../utils";
import { setupDesktopIntegration } from "../utils/desktop-integration";
import { rm } from "fs/promises";
import { disableCustomDns, enableCustomDns } from "../utils/custom-dns";
const t = initTRPC.create();
@@ -56,15 +55,6 @@ export const osIntegrationRouter = t.router({
config.zoomFactor = factor;
}),
customDns: t.procedure.query(() => config.customDns),
setCustomDns: t.procedure
.input(z.boolean().optional())
.mutation(({ input: customDns }) => {
if (customDns) enableCustomDns();
else disableCustomDns();
config.customDns = !!customDns;
}),
proxyRules: t.procedure.query(() => config.proxyRules),
setProxyRules: t.procedure
.input(z.string().optional())
@@ -152,10 +142,6 @@ export const osIntegrationRouter = t.router({
await rm(input);
}),
restart: t.procedure.query(() => {
app.relaunch();
app.exit();
}),
showNotification: t.procedure
.input(NotificationOptions)
.query(({ input }) => {
@@ -184,45 +170,16 @@ export const osIntegrationRouter = t.router({
}),
bringToFront: t.procedure.query(() => bringToFront()),
changeTheme: t.procedure
.input(
z.object({
theme: Theme,
windowControlsIconColor: z.string().optional(),
backgroundColor: z.string().optional()
})
)
.mutation(
({ input: { theme, windowControlsIconColor, backgroundColor } }) => {
if (windowControlsIconColor) {
config.windowControlsIconColor = windowControlsIconColor;
if (
process.platform === "win32" &&
!config.desktopSettings.nativeTitlebar
)
globalThis.window?.setTitleBarOverlay({
symbolColor: windowControlsIconColor
});
}
if (backgroundColor) {
config.backgroundColor = backgroundColor;
}
setTheme(theme);
}
),
.input(Theme)
.mutation(({ input }) => setTheme(input)),
onThemeChanged: t.procedure.subscription(() =>
observable<"dark" | "light">((emit) => {
const updated = () => {
nativeTheme.on("updated", () => {
if (getTheme() === "system") {
emit.next(nativeTheme.shouldUseDarkColors ? "dark" : "light");
}
};
nativeTheme.on("updated", updated);
return () => {
nativeTheme.off("updated", updated);
};
});
})
)
});

View File

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

View File

@@ -28,38 +28,16 @@ type UpdateInfo = { version: string };
type Progress = { percent: number };
const t = initTRPC.create();
let cancellationToken: CancellationToken | undefined = undefined;
let downloadTimeout: NodeJS.Timeout | undefined = undefined;
export const updaterRouter = t.router({
autoUpdates: t.procedure.query(() => config.automaticUpdates),
install: t.procedure.query(() => autoUpdater.quitAndInstall()),
download: t.procedure.query(async () => {
if (cancellationToken) return;
clearTimeout(downloadTimeout);
await new Promise<string[]>((resolve, reject) => {
downloadTimeout = setTimeout(async () => {
cancellationToken = new CancellationToken();
autoUpdater.isUpdaterActive();
await autoUpdater
.downloadUpdate(cancellationToken)
.then(resolve)
.catch(reject)
.finally(() => (cancellationToken = undefined));
}, 1000);
});
const cancellationToken = new CancellationToken();
await autoUpdater.downloadUpdate(cancellationToken);
}),
check: t.procedure.query(async () => {
if (cancellationToken) return;
clearTimeout(downloadTimeout);
await new Promise<void>((resolve) => {
downloadTimeout = setTimeout(async () => {
await autoUpdater
.checkForUpdates()
.catch(console.error)
.finally(resolve);
}, 1000);
});
await autoUpdater.checkForUpdates();
}),
toggleAutoUpdates: t.procedure
@@ -79,18 +57,7 @@ export const updaterRouter = t.router({
"update-not-available"
),
onAvailable: createSubscription<"update-available", UpdateInfo>(
"update-available",
() => {
if (!config.automaticUpdates) return false;
autoUpdater.emit("download-progress", {
bytesPerSecond: 0,
delta: 0,
percent: 0,
total: 100,
transferred: 0
});
return true;
}
"update-available"
),
onError: createSubscription("error")
});
@@ -98,14 +65,12 @@ export const updaterRouter = t.router({
function createSubscription<
TName extends keyof AppUpdaterEvents,
TReturnType = Parameters<AppUpdaterEvents[TName]>[0]
>(eventName: TName, handler?: (args: TReturnType) => boolean) {
>(eventName: TName) {
return t.procedure.subscription(() => {
return observable<TReturnType>((emit) => {
const listener: AppUpdaterEvents[TName] = (...args: any[]) => {
if (handler?.(args[0])) return;
emit.next(args[0]);
};
autoUpdater.removeAllListeners(eventName);
autoUpdater.addListener(eventName, listener);
return () => {
autoUpdater.removeListener(eventName, listener);

View File

@@ -1,83 +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 { initTRPC } from "@trpc/server";
import { observable } from "@trpc/server/observable";
const t = initTRPC.create();
export const windowRouter = t.router({
maximize: t.procedure.mutation(() => {
globalThis.window?.maximize();
}),
restore: t.procedure.mutation(() => {
globalThis.window?.restore();
}),
minimze: t.procedure.mutation(() => {
globalThis.window?.minimize();
}),
maximized: t.procedure.query(() => globalThis.window?.isMaximized()),
fullscreen: t.procedure.query(() => globalThis.window?.isFullScreen()),
onWindowStateChanged: t.procedure.subscription(() => {
return observable<{ maximized: boolean; fullscreen: boolean }>((emit) => {
function listener() {
emit.next({
maximized: !!globalThis.window?.isMaximized(),
fullscreen: !!globalThis.window?.isFullScreen()
});
}
function enterFullscreen() {
emit.next({
maximized: !!globalThis.window?.isMaximized(),
fullscreen: true
});
}
function leaveFullscreen() {
emit.next({
maximized: !!globalThis.window?.isMaximized(),
fullscreen: false
});
}
globalThis.window?.addListener("maximize", listener);
globalThis.window?.addListener("minimize", listener);
globalThis.window?.addListener("unmaximize", listener);
globalThis.window?.addListener("restore", listener);
globalThis.window?.addListener("enter-full-screen", enterFullscreen);
globalThis.window?.addListener("leave-full-screen", leaveFullscreen);
globalThis.window?.addListener("leave-html-full-screen", leaveFullscreen);
globalThis.window?.addListener("enter-html-full-screen", enterFullscreen);
return () => {
globalThis.window?.removeListener("maximize", listener);
globalThis.window?.removeListener("minimize", listener);
globalThis.window?.removeListener("unmaximize", listener);
globalThis.window?.removeListener("restore", listener);
globalThis.window?.removeListener("enter-full-screen", enterFullscreen);
globalThis.window?.removeListener("leave-full-screen", leaveFullscreen);
globalThis.window?.removeListener(
"leave-html-full-screen",
leaveFullscreen
);
globalThis.window?.removeListener(
"enter-html-full-screen",
enterFullscreen
);
};
});
})
});

View File

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

View File

@@ -35,7 +35,6 @@ import path from "path";
import { bringToFront } from "./utils/bring-to-front";
import { bridge } from "./api/bridge";
import { setupDesktopIntegration } from "./utils/desktop-integration";
import { disableCustomDns, enableCustomDns } from "./utils/custom-dns";
// only run a single instance
if (!MAC_APP_STORE && !app.requestSingleInstanceLock()) {
@@ -83,28 +82,10 @@ async function createWindow() {
size: 512,
format: process.platform === "win32" ? "ico" : "png"
}),
...(config.desktopSettings.nativeTitlebar
? {}
: {
titleBarStyle: "hidden",
frame: process.platform === "win32" || process.platform === "darwin",
titleBarOverlay: {
height: 37,
color: "#00000000",
symbolColor: config.windowControlsIconColor
},
trafficLightPosition: {
x: 16,
y: 12
}
}),
webPreferences: {
zoomFactor: config.zoomFactor,
nodeIntegration: true,
contextIsolation: false,
nodeIntegrationInWorker: true,
spellcheck: config.isSpellCheckerEnabled,
preload: __dirname + "/preload.js"
}
@@ -141,7 +122,7 @@ async function createWindow() {
setupJumplist();
if (isDevelopment())
mainWindow.webContents.openDevTools({ mode: "bottom", activate: true });
mainWindow.webContents.openDevTools({ mode: "right", activate: true });
mainWindow.webContents.setWindowOpenHandler((details) => {
shell.openExternal(details.url);
@@ -157,9 +138,6 @@ async function createWindow() {
app.once("ready", async () => {
console.info("App ready. Opening window.");
if (config.customDns) enableCustomDns();
else disableCustomDns();
if (!isDevelopment()) registerProtocol();
await createWindow();
configureAutoUpdater();

View File

@@ -20,14 +20,16 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
import { ELECTRON_TRPC_CHANNEL } from "electron-trpc/main";
import { type RendererGlobalElectronTRPC } from "electron-trpc/src/types";
// import type { NNCrypto } from "@notesnook/crypto";
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 NativeNNCrypto: (new () => NNCrypto) | undefined;
var createSQLite3Database: (filename: string) => Database;
}
process.once("loaded", async () => {
@@ -40,5 +42,6 @@ process.once("loaded", async () => {
globalThis.electronTRPC = electronTRPC;
});
// globalThis.NativeNNCrypto = require("@notesnook/crypto").NNCrypto;
globalThis.NativeNNCrypto = require("@notesnook/crypto").NNCrypto;
globalThis.createSQLite3Database = (filename) => sqlite3(filename);
globalThis.os = () => (MAC_APP_STORE ? "mas" : platform());

View File

@@ -20,7 +20,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
import { NativeImage, nativeImage } from "electron";
import path from "path";
import { isDevelopment } from "./index";
import { ParsedImage, parseICO } from "icojs";
import { parse, ParsedImage } from "icojs";
import { getSystemTheme } from "./theme";
import { readFile } from "fs/promises";
@@ -71,7 +71,7 @@ export class AssetManager {
`${icon}${prefix}.ico`
);
const icoBuffer = await readFile(icoPath);
const images = await parseICO(icoBuffer, "image/png");
const images = await parse(icoBuffer, "image/png");
ALL_ICONS.push({ id: icon, images, prefix });
}
}

View File

@@ -35,7 +35,6 @@ async function configureAutoUpdater() {
autoUpdater.allowDowngrade = false;
autoUpdater.allowPrerelease = false;
autoUpdater.autoInstallOnAppQuit = true;
autoUpdater.disableWebInstaller = true;
}
export { configureAutoUpdater };

View File

@@ -25,8 +25,7 @@ export const DesktopIntegration = z.object({
autoStart: z.boolean().optional(),
startMinimized: z.boolean().optional(),
minimizeToSystemTray: z.boolean().optional(),
closeToSystemTray: z.boolean().optional(),
nativeTitlebar: z.boolean().optional()
closeToSystemTray: z.boolean().optional()
});
export type DesktopIntegration = z.infer<typeof DesktopIntegration>;
@@ -36,20 +35,14 @@ export const config = {
autoStart: false,
startMinimized: false,
minimizeToSystemTray: false,
closeToSystemTray: false,
nativeTitlebar: false
closeToSystemTray: false
},
privacyMode: false,
isSpellCheckerEnabled: true,
zoomFactor: 1,
theme: nativeTheme.themeSource,
automaticUpdates: true,
proxyRules: "",
customDns: true,
backgroundColor: nativeTheme.themeSource === "dark" ? "#0f0f0f" : "#ffffff",
windowControlsIconColor:
nativeTheme.themeSource === "dark" ? "#ffffff" : "#000000"
proxyRules: ""
};
type ConfigKey = keyof typeof config;

View File

@@ -1,37 +0,0 @@
/*
This file is part of the Notesnook project (https://notesnook.com/)
Copyright (C) 2023 Streetwriters (Private) Limited
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { app } from "electron";
export function enableCustomDns() {
app.configureHostResolver({
secureDnsServers: [
"https://mozilla.cloudflare-dns.com/dns-query",
"https://dns.quad9.net/dns-query"
],
enableBuiltInResolver: true
});
}
export function disableCustomDns() {
app.configureHostResolver({
secureDnsServers: [],
enableBuiltInResolver: true
});
}

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 { net, protocol } from "electron";
import { protocol } from "electron";
import { isDevelopment } from "./index";
import { createReadStream } from "fs";
import { extname, normalize } from "path";
@@ -62,7 +62,26 @@ function registerProtocol() {
headers: { "Content-Type": extensionToMimeType[fileExtension] }
});
} else {
return net.fetch(request, { bypassCustomProtocolHandlers: true });
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 "successful"`);

View File

@@ -41,10 +41,7 @@ function setTheme(theme: Theme) {
}
function getBackgroundColor() {
return (
config.backgroundColor ||
(nativeTheme.shouldUseDarkColors ? "#0f0f0f" : "#ffffff")
);
return nativeTheme.shouldUseDarkColors ? "#0f0f0f" : "#ffffff";
}
function getSystemTheme() {

View File

@@ -38,7 +38,7 @@ DerivedData
*.ipa
*.xcuserstate
*.hprof
**/.xcode.env.local
ios/.xcode.env.local
# Android/IntelliJ
#
rn-build-deps/

View File

@@ -16,13 +16,13 @@ GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import "react-native-gesture-handler";
import {
THEME_COMPATIBILITY_VERSION,
useThemeEngineStore
} from "@notesnook/theme";
import React, { useEffect } from "react";
import { I18nManager, View } from "react-native";
import "react-native-gesture-handler";
import { GestureHandlerRootView } from "react-native-gesture-handler";
import { SafeAreaProvider } from "react-native-safe-area-context";
import AppLockedOverlay from "./components/app-lock-overlay";
@@ -40,25 +40,30 @@ import { useUserStore } from "./stores/use-user-store";
I18nManager.allowRTL(false);
I18nManager.forceRTL(false);
I18nManager.swapLeftAndRightInRTL(false);
const { appLockEnabled, appLockMode } = SettingsService.get();
if (appLockEnabled || appLockMode !== "none") {
useUserStore.getState().lockApp(true);
}
const App = () => {
useAppEvents();
//@ts-ignore
globalThis["IS_MAIN_APP_RUNNING"] = true;
const init = useAppEvents();
useEffect(() => {
SettingsService.onFirstLaunch();
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={{

Binary file not shown.

Before

Width:  |  Height:  |  Size: 112 KiB

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 108 KiB

After

Width:  |  Height:  |  Size: 28 KiB

View File

@@ -0,0 +1,156 @@
/*
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 { Platform } from "react-native";
import { MMKV } from "../database/mmkv";
import { useSettingStore } from "../../stores/use-setting-store";
const WEBSITE_ID = "3c6890ce-8410-49d5-8831-15fb2eb28a21";
const baseUrl = "https://analytics.streetwriters.co/api/collect";
const UA =
Platform.OS === "ios"
? "Mozilla/5.0 (iPhone; CPU iPhone OS 12_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/12.0 Mobile/15E148 Safari/604.1"
: `
Mozilla/5.0 (Linux; Android 8.0.0; SM-G960F Build/R16NW) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.84 Mobile Safari/537.36`;
/**
*
* @param {Routes} route
* @param {Routes[]} conditions
* @returns
*/
async function canUpdateAnalytics(route, conditions = []) {
if (!useSettingStore?.getState()?.settings?.telemetry) return false;
let eventsList = MMKV.getString("notesnookUserEvents");
if (eventsList) {
eventsList = JSON.parse(eventsList);
}
if (eventsList && eventsList[route]) {
console.log("analytics: event already sent", route);
return false;
}
if (route !== "/welcome") {
for (let cond of conditions) {
if (!eventsList || !eventsList[cond]) {
console.log("analytics: conditions not met for event", route, cond);
return false;
}
}
}
console.log("analytics: will send event", route);
return true;
}
async function saveAnalytics(route, value = true) {
let eventsList = MMKV.getString("notesnookUserEvents");
if (eventsList) {
eventsList = JSON.parse(eventsList);
} else {
eventsList = {};
}
eventsList[route] = value;
MMKV.setString("notesnookUserEvents", JSON.stringify(eventsList));
}
/**
*@typedef {"/welcome" | "/home" | "/signup" | "/first-note" | "/account-created" | "/pro-sheet" | "/pro-plans" | "/iap-native" | "/pro-screen" | "/editor" | "/editor-toolbar" | "/properties" | "/sidemenu"} Routes
* @param {Routes} prevRoute
* @param {Routes} route
* @param {Routes[]} conditions
* @param {boolean} once
* @returns
*/
async function pageView(
route,
prevRoute = "",
conditions = ["/welcome"],
once = true
) {
if (__DEV__) return;
if (!(await canUpdateAnalytics(route, conditions)) && once) return;
let body = {
payload: {
website: WEBSITE_ID,
url: `notesnook-${Platform.OS}${prevRoute}${route}`,
referrer: `https://notesnook.com/notesnook-${Platform.OS}${prevRoute}`,
hostname: `notesnook-${Platform.OS}`,
language: "en-US",
screen: "1920x1080"
},
type: "pageview"
};
try {
let response = await fetch(baseUrl, {
method: "POST",
headers: {
"Content-Type": "application/json",
"User-Agent": UA
},
body: JSON.stringify(body)
});
console.log("analytics: event sent", route);
await saveAnalytics(route);
return await response.text();
} catch (e) {
console.log(e);
}
}
async function sendEvent(type, value, once = true) {
if (__DEV__) return;
if (!(await canUpdateAnalytics(type)) && once) return;
let body = {
payload: {
website: WEBSITE_ID,
url: "/",
event_type: type,
event_value: value,
hostname: "notesnook-android-app",
language: "en-US",
screen: "1920x1080"
},
type: "event"
};
try {
let response = await fetch(baseUrl, {
method: "POST",
headers: {
"Content-Type": "application/json",
"User-Agent": UA
},
body: JSON.stringify(body)
});
console.log(response.status);
return await response.text();
} catch (e) {
console.log(e);
}
}
export default {
sendEvent,
pageView,
saveAnalytics
};

View File

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

View File

@@ -18,21 +18,39 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import "./logger";
import { database } from "@notesnook/common";
import { logger as dbLogger } from "@notesnook/core";
import { logger as dbLogger } from "@notesnook/core/dist/logger";
import { Platform } from "react-native";
import * as Gzip from "react-native-gzip";
import EventSource from "../../utils/sse/even-source-ios";
import AndroidEventSource from "../../utils/sse/event-source";
import {
SqliteAdapter,
SqliteIntrospector,
SqliteQueryCompiler
} from "@streetwriters/kysely";
import { SqliteAdapter, SqliteIntrospector, SqliteQueryCompiler } from "kysely";
import filesystem from "../filesystem";
import Storage from "./storage";
import { RNSqliteDriver } from "./sqlite.kysely";
import { getDatabaseKey } from "./encryption";
import SettingsService from "../../services/settings";
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"
}
);
export async function setupDatabase(password) {
const key = await getDatabaseKey(password);
@@ -41,15 +59,6 @@ export async function setupDatabase(password) {
console.log("Opening database with key:", !!key);
database.host({
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",
...(SettingsService.getProperty("serverUrls") || {})
});
database.setup({
storage: Storage,
eventsource: Platform.OS === "ios" ? EventSource : AndroidEventSource,
@@ -76,10 +85,4 @@ export async function setupDatabase(password) {
}
export const db = database;
let DatabaseLogger = dbLogger.scope(Platform.OS);
const setLogger = () => {
DatabaseLogger = dbLogger.scope(Platform.OS);
};
export { DatabaseLogger, setLogger };
export const DatabaseLogger = dbLogger;

View File

@@ -16,34 +16,14 @@ GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { MMKVLoader } from "react-native-mmkv-storage";
import { initialize } from "@notesnook/core/dist/logger";
import { KV } from "./storage";
import { initialize } from "@notesnook/core";
import {
SqliteAdapter,
SqliteIntrospector,
SqliteQueryCompiler
} from "@streetwriters/kysely";
import { Platform } from "react-native";
import { setLogger } from ".";
import { RNSqliteDriver } from "./sqlite.kysely";
const LoggerStorage = new MMKVLoader()
.withInstanceID("notesnook_logs")
.initialize();
let loggerLoaded = false;
const initializeLogger = async () => {
if (loggerLoaded) return;
await initialize({
dialect: (name) => ({
createDriver: () => {
return new RNSqliteDriver({ async: true, dbName: name });
},
createAdapter: () => new SqliteAdapter(),
createIntrospector: (db) => new SqliteIntrospector(db),
createQueryCompiler: () => new SqliteQueryCompiler()
}),
tempStore: "memory",
journalMode: Platform.OS === "ios" ? "DELETE" : "WAL"
});
setLogger();
loggerLoaded = true;
};
initialize(new KV(LoggerStorage));
export { initializeLogger };
export { LoggerStorage };

View File

@@ -17,12 +17,8 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import type {
DatabaseConnection,
Driver,
QueryResult
} from "@streetwriters/kysely";
import { CompiledQuery } from "@streetwriters/kysely";
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 };

View File

@@ -21,15 +21,15 @@ import { Platform } from "react-native";
import RNFetchBlob from "react-native-blob-util";
import {
decrypt,
decryptMulti,
deriveCryptoKey,
encrypt,
encryptMulti,
generateCryptoKey,
getCryptoKey,
getRandomBytes,
hash,
removeCryptoKey
removeCryptoKey,
decryptMulti,
encryptMulti
} from "./encryption";
import { MMKV } from "./mmkv";

View File

@@ -20,6 +20,7 @@ import { Dimensions, Image, Platform } from "react-native";
import ImageResizer from "@bam.tech/react-native-image-resizer";
import RNFetchBlob from "react-native-blob-util";
console.log("hello world");
/**
* Scale down & compress images to screen width
* for loading in editor.

View File

@@ -18,20 +18,24 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import Sodium from "@ammarahmed/react-native-sodium";
import { getFileNameWithExtension } from "@notesnook/core";
import {
getFileNameWithExtension,
isImage,
isDocument
} from "@notesnook/core/dist/utils/filename";
import React from "react";
import { Platform } from "react-native";
import RNFetchBlob from "react-native-blob-util";
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 { ToastManager, presentSheet } from "../../services/event-manager";
import { useAttachmentStore } from "../../stores/use-attachment-store";
import { IOS_APPGROUPID } from "../../utils/constants";
import { DatabaseLogger, db } from "../database";
import Storage from "../database/storage";
import { createCacheDir, exists } from "./io";
import { cacheDir, copyFileAsync, releasePermissions } from "./utils";
import { createCacheDir, exists } from "./io";
import { IOS_APPGROUPID } from "../../utils/constants";
export const FileDownloadStatus = {
Success: 1,
@@ -54,10 +58,15 @@ export async function downloadAllAttachments() {
* @param onProgress
* @returns
*/
export async function downloadAttachments(attachments) {
export async function downloadAttachments(
attachments,
onProgress,
canceled,
groupId
) {
await createCacheDir();
if (!attachments || !attachments.length) return;
const groupId = `download-all-${Date.now()}`;
const result = new Map();
let outputFolder;
if (Platform.OS === "android") {
@@ -75,123 +84,96 @@ export async function downloadAttachments(attachments) {
Platform.OS === "ios"
? `${outputFolder}/notesnook-attachments-${Date.now()}.zip`
: `${cacheDir}/notesnook-attachments.zip`;
if (await RNFetchBlob.fs.exists(zipSourceFolder)) {
if (await RNFetchBlob.fs.exists(zipSourceFolder))
await RNFetchBlob.fs.unlink(zipSourceFolder);
}
await RNFetchBlob.fs.mkdir(zipSourceFolder);
const isCancelled = () => {
if (useAttachmentStore.getState().downloading[groupId]?.canceled) {
RNFetchBlob.fs.unlink(zipSourceFolder).catch(console.log);
useAttachmentStore.getState().setDownloading({
groupId,
current: 0,
total: 0,
success: false,
message: "Download cancelled",
canceled: true
});
return true;
}
};
for (let i = 0; i < attachments.length; i++) {
if (isCancelled()) return;
let attachment = await db.attachments.attachment(attachments[i]);
const hash = attachment.hash;
try {
useAttachmentStore.getState().setDownloading({
groupId: groupId,
current: i + 1,
total: attachments.length,
filename: attachment.hash
});
if (canceled.current) {
RNFetchBlob.fs.unlink(zipSourceFolder).catch(console.log);
return;
}
onProgress?.(
i + 1 / attachments.length,
`Downloading attachments (${i + 1}/${
attachments.length
})... Please wait`
);
// Download to cache
let uri = await downloadAttachment(hash, false, {
silent: true,
cache: true,
groupId: groupId
});
if (isCancelled()) return;
if (canceled.current) {
RNFetchBlob.fs.unlink(zipSourceFolder).catch(console.log);
return;
}
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.filename}`;
await RNFetchBlob.fs.mv(`${cacheDir}/${uri}`, filePath);
} catch (e) {
DatabaseLogger.error(e);
}
}
useAttachmentStore.getState().setDownloading({
groupId: groupId,
current: 0,
total: 0,
success: true
});
if (isCancelled()) return;
let sub;
try {
useAttachmentStore.getState().setDownloading({
current: 0,
total: 1,
message: "Saving zip file... Please wait",
groupId
});
// If all goes well, zip the notesnook-attachments folder in cache.
sub = subscribe(({ progress }) => {
useAttachmentStore.getState().setDownloading({
groupId,
current: progress,
total: 1,
message: `Saving zip file (${(progress * 100).toFixed(
1
)}%)... Please wait`
result.set(hash, {
filename: attachment.filename,
status: FileDownloadStatus.Success,
attachment: attachment
});
});
await zip(zipSourceFolder, zipOutputFile);
sub?.remove();
if (Platform.OS === "android") {
// Move the zip to user selected directory.
const file = await ScopedStorage.createFile(
outputFolder,
`notesnook-attachments-${Date.now()}.zip`,
"application/zip"
);
await copyFileAsync(`file://${zipOutputFile}`, file.uri);
} catch (e) {
result.set(hash, {
filename: attachment.filename,
status: FileDownloadStatus.Fail,
reason: e
});
console.log("Error downloading attachment", e);
}
}
if (canceled.current) {
RNFetchBlob.fs.unlink(zipSourceFolder).catch(console.log);
return;
}
if (result?.size) {
let sub;
try {
onProgress?.(0, `Zipping... Please wait`);
// If all goes well, zip the notesnook-attachments folder in cache.
useAttachmentStore.getState().setDownloading({
current: 0,
total: 0,
message: undefined,
success: true,
groupId
});
releasePermissions(outputFolder);
} catch (e) {
useAttachmentStore.getState().setDownloading({
current: 0,
total: 0,
message: undefined,
success: false,
groupId
});
releasePermissions(outputFolder);
sub?.remove();
ToastManager.error(e, "Error zipping attachments");
}
// Remove source & zip file from cache.
RNFetchBlob.fs.unlink(zipSourceFolder).catch(console.log);
if (Platform.OS === "android") {
RNFetchBlob.fs.unlink(zipOutputFile).catch(console.log);
sub = subscribe(({ progress }) => {
onProgress(
progress,
`Saving zip file (${(progress * 100).toFixed(1)}%)... Please wait`
);
});
await zip(zipSourceFolder, zipOutputFile);
sub?.remove();
onProgress(1, `Saving zip file... Please wait`);
if (Platform.OS === "android") {
// Move the zip to user selected directory.
const file = await ScopedStorage.createFile(
outputFolder,
`notesnook-attachments-${Date.now()}.zip`,
"application/zip"
);
await copyFileAsync(`file://${zipOutputFile}`, file.uri);
}
onProgress?.(1, `Done`);
releasePermissions(outputFolder);
} catch (e) {
releasePermissions(outputFolder);
sub?.remove();
console.log("Error zipping attachments", e);
}
// Remove source & zip file from cache.
RNFetchBlob.fs.unlink(zipSourceFolder).catch(console.log);
if (Platform.OS === "android") {
RNFetchBlob.fs.unlink(zipOutputFile).catch(console.log);
}
}
return result;
}
export default async function downloadAttachment(
@@ -227,18 +209,19 @@ export default async function downloadAttachment(
try {
await db
.fs()
.downloadFile(
options.groupId || attachment.hash,
attachment.hash,
attachment.chunkSize
);
.downloadFile(options.groupId || attachment.hash, attachment.hash);
if (!(await exists(attachment.hash))) {
DatabaseLogger.log("Attachment does not exist after download.");
return;
}
if (options.base64 || options.text) {
DatabaseLogger.log(`Starting to decrypt... hash: ${attachment.hash}`);
console.log(
"starting decrypt base64 file...",
options.base64,
options.text,
attachment.hash
);
return await db.attachments.read(
attachment.hash,
options.base64 ? "base64" : "text"
@@ -279,6 +262,15 @@ export default async function downloadAttachment(
});
}
if (
attachment.dateUploaded &&
!isImage(attachment.mimeType) &&
!isDocument(attachment.mimeType)
) {
RNFetchBlob.fs
.unlink(RNFetchBlob.fs.dirs.CacheDir + `/${attachment.hash}`)
.catch(console.log);
}
if (Platform.OS === "ios" && !options.cache) {
fileUri = folder.uri + `/${filename}`;
}
@@ -298,6 +290,7 @@ export default async function downloadAttachment(
return fileUri;
} catch (e) {
console.log("download attachment error: ", e);
if (attachment.dateUploaded) {
RNFetchBlob.fs
.unlink(RNFetchBlob.fs.dirs.CacheDir + `/${attachment.hash}`)
@@ -306,7 +299,6 @@ export default async function downloadAttachment(
.unlink(RNFetchBlob.fs.dirs.CacheDir + `/${attachment.hash}_dcache`)
.catch(console.log);
}
DatabaseLogger.error(e);
useAttachmentStore.getState().remove(attachment.hash);
if (options.throwError) {
throw e;

View File

@@ -17,127 +17,64 @@ 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/dist/utils/constants";
import NetInfo from "@react-native-community/netinfo";
import RNFetchBlob from "react-native-blob-util";
import { ToastManager } from "../../services/event-manager";
import { useAttachmentStore } from "../../stores/use-attachment-store";
import { DatabaseLogger, db } from "../database";
import { db } from "../database";
import { cacheDir, fileCheck } from "./utils";
import { createCacheDir, exists } from "./io";
import { ABYTES, cacheDir, getUploadedFileSize, parseS3Error } from "./utils";
export async function downloadFile(filename, requestOptions, cancelToken) {
if (!requestOptions) {
DatabaseLogger.log(
`Error downloading file: ${filename}, reason: No requestOptions`
);
return false;
}
export async function downloadFile(filename, data, cancelToken) {
if (!data) return false;
DatabaseLogger.log(`Downloading ${filename}`);
console.log("Downloading", filename);
await createCacheDir();
let { url, headers, chunkSize } = requestOptions;
let tempFilePath = `${cacheDir}/${filename}_temp`;
let originalFilePath = `${cacheDir}/${filename}`;
let { url, headers } = data;
let path = `${cacheDir}/${filename}`;
try {
if (await exists(filename)) {
DatabaseLogger.log(`File Exists already: ${filename}`);
console.log("Exists already", filename);
return true;
}
const attachment = await db.attachments.attachment(filename);
const size = await getUploadedFileSize(filename);
if (size === -1) {
const error = `Uploaded file verification failed. (File hash: ${filename})`;
throw new Error(error);
}
if (size === 0) {
const error = `File length is 0. Please upload this file again from the attachment manager. (File hash: ${filename})`;
await db.attachments.markAsFailed(attachment.id, error);
throw new Error(error);
}
const totalChunks = Math.ceil(size / chunkSize);
const decryptedLength = size - totalChunks * ABYTES;
if (attachment && attachment.size !== decryptedLength) {
const error = `File length mismatch. Expected ${attachment.size} but got ${decryptedLength} bytes. Please upload this file again from the attachment manager. (File hash: ${filename})`;
await db.attachments.markAsFailed(attachment.id, error);
throw new Error(error);
}
let res = await fetch(url, {
method: "GET",
headers
});
if (!res.ok) {
DatabaseLogger.log(
`Error downloading file: ${filename}, ${res.status}, ${res.statusText}, reason: Unable to resolve download url`
);
if (!res.ok)
throw new Error(`${res.status}: Unable to resolve download url`);
}
const downloadUrl = await res.text();
if (!downloadUrl) {
DatabaseLogger.log(
`Error downloading file: ${filename}, reason: Unable to resolve download url`
);
throw new Error("Unable to resolve download url");
}
DatabaseLogger.log(`Download starting: ${filename}`);
if (!downloadUrl) throw new Error("Unable to resolve download url");
let totalSize = 0;
console.log("Download starting");
let request = RNFetchBlob.config({
path: tempFilePath,
IOSBackgroundTask: true,
overwrite: true
path: path,
IOSBackgroundTask: true
})
.fetch("GET", downloadUrl, null)
.progress(async (recieved, total) => {
.progress((recieved, total) => {
useAttachmentStore
.getState()
.setProgress(0, total, filename, recieved, "download");
DatabaseLogger.log(`Downloading: ${filename}, ${recieved}/${total}`);
totalSize = total;
console.log("downloading: ", recieved, total);
});
cancelToken.cancel = () => {
useAttachmentStore.getState().remove(filename);
request.cancel();
RNFetchBlob.fs.unlink(tempFilePath).catch(console.log);
DatabaseLogger.log(`Download cancelled: ${filename}`);
};
let response = await request;
console.log(response.info().headers);
const contentType =
response.info().headers?.["content-type"] ||
response.info().headers?.["Content-Type"];
if (contentType === "application/xml") {
const error = parseS3Error(await response.text());
throw new Error(`[${error.Code}] ${error.Message}`);
}
await fileCheck(response, totalSize);
let status = response.info().status;
useAttachmentStore.getState().remove(filename);
if (exists(originalFilePath)) {
await RNFetchBlob.fs.unlink(originalFilePath).catch(console.log);
}
await RNFetchBlob.fs.mv(tempFilePath, originalFilePath);
if (!(await exists(filename))) {
throw new Error("File size mismatch");
}
return status >= 200 && status < 300;
} catch (e) {
if (e.message !== "canceled" && !e.message.includes("NoSuchKey")) {
if (e.message !== "canceled") {
ToastManager.show({
heading: "Error downloading file",
message: e.message,
@@ -153,33 +90,42 @@ export async function downloadFile(filename, requestOptions, cancelToken) {
}
useAttachmentStore.getState().remove(filename);
RNFetchBlob.fs.unlink(tempFilePath).catch(console.log);
RNFetchBlob.fs.unlink(originalFilePath).catch(console.log);
DatabaseLogger.error(e, {
url,
headers
});
RNFetchBlob.fs.unlink(path).catch(console.log);
console.log("Download file error:", e, url, headers);
return false;
}
}
export async function getUploadedFileSize(hash) {
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 contentLength = parseInt(
attachmentInfo.headers?.get("content-length")
);
return isNaN(contentLength) ? 0 : contentLength;
} catch (e) {
return 0;
}
}
export async function checkAttachment(hash) {
const internetState = await NetInfo.fetch();
const isInternetReachable =
internetState.isConnected && internetState.isInternetReachable;
if (!isInternetReachable) return;
if (!isInternetReachable) return { success: true };
const attachment = await db.attachments.attachment(hash);
if (!attachment) return { failed: "Attachment not found." };
try {
const size = await getUploadedFileSize(hash);
console.log("File Size", size);
if (size === -1) return { success: true };
if (size === 0)
return {
failed: `File length is 0. Please upload this file again from the attachment manager. (File hash: ${hash})`
};
if (size <= 0) return { failed: "File length is 0." };
} catch (e) {
return { failed: e?.message };
}

View File

@@ -17,22 +17,17 @@ 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 { checkAttachment, downloadFile } from "./download";
import { checkAttachment, downloadFile, getUploadedFileSize } from "./download";
import {
clearFileStorage,
deleteFile,
exists,
readEncrypted,
writeEncryptedBase64,
hashBase64,
clearCache,
deleteCacheFileByName,
deleteCacheFileByPath,
bulkExists,
getCacheSize
hashBase64
} from "./io";
import { uploadFile } from "./upload";
import { cancelable, getUploadedFileSize } from "./utils";
import { cancelable } from "./utils";
export default {
readEncrypted,
@@ -44,10 +39,5 @@ export default {
exists,
clearFileStorage,
getUploadedFileSize,
checkAttachment,
clearCache,
deleteCacheFileByName,
deleteCacheFileByPath,
bulkExists,
getCacheSize
checkAttachment
};

View File

@@ -20,14 +20,13 @@ 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 RNFetchBlob from "react-native-blob-util";
import { eSendEvent } from "../../services/event-manager";
import { IOS_APPGROUPID } from "../../utils/constants";
import { DatabaseLogger, db } from "../database";
import { ABYTES, cacheDir, cacheDirOld, getRandomId } from "./utils";
import { db } from "../database";
import { cacheDir, cacheDirOld, getRandomId } from "./utils";
export async function readEncrypted(filename, key, cipherData) {
await migrateFilesFromCache();
DatabaseLogger.log("Read encrypted file...");
console.log("Read encrypted file...");
let path = `${cacheDir}/${filename}`;
try {
@@ -35,6 +34,8 @@ export async function readEncrypted(filename, key, cipherData) {
return false;
}
const attachment = await db.attachments.attachment(filename);
console.log("decrypting....");
let output = await Sodium.decryptFile(
key,
{
@@ -44,13 +45,12 @@ export async function readEncrypted(filename, key, cipherData) {
},
cipherData.outputType === "base64" ? "base64" : "text"
);
DatabaseLogger.log("File decrypted...");
console.log("file decrypted...", attachment?.mimeType);
return output;
} catch (e) {
RNFetchBlob.fs.unlink(path).catch(console.log);
DatabaseLogger.error(e);
console.log("readEncrypted", e);
return false;
}
}
@@ -67,7 +67,7 @@ export async function hashBase64(data) {
};
}
export async function writeEncryptedBase64(data, key) {
export async function writeEncryptedBase64({ data, key }) {
await createCacheDir();
let filepath = cacheDir + `/${getRandomId("imagecache_")}`;
await RNFetchBlob.fs.writeFile(filepath, data, "base64");
@@ -77,8 +77,6 @@ export async function writeEncryptedBase64(data, key) {
});
RNFetchBlob.fs.unlink(filepath).catch(console.log);
console.log("encrypted file output: ", output);
output.size = output.length;
delete output.length;
return {
...output,
alg: "xcha-stream"
@@ -128,7 +126,7 @@ export async function clearFileStorage() {
export async function createCacheDir() {
if (!(await RNFetchBlob.fs.exists(cacheDir))) {
await RNFetchBlob.fs.mkdir(cacheDir);
DatabaseLogger.log("Cache directory created");
console.log("Cache directory created");
}
}
@@ -159,35 +157,7 @@ export async function migrateFilesFromCache() {
}
}
export async function clearCache() {
await RNFetchBlob.fs.unlink(cacheDir).catch(console.log);
await createCacheDir();
eSendEvent("cache-cleared");
}
export async function deleteCacheFileByPath(path) {
await RNFetchBlob.fs.unlink(path).catch(console.log);
}
export async function deleteCacheFileByName(name) {
const iosAppGroup =
Platform.OS === "ios"
? await RNFetchBlob.fs.pathForAppGroup(IOS_APPGROUPID)
: null;
const appGroupPath = `${iosAppGroup}/${name}`;
await RNFetchBlob.fs.unlink(appGroupPath).catch(console.log);
await RNFetchBlob.fs.unlink(`${cacheDir}/${name}`).catch(console.log);
}
export async function deleteDCacheFiles() {
const files = await RNFetchBlob.fs.ls(cacheDir);
for (const file of files) {
if (file.includes("_dcache")) {
await RNFetchBlob.fs.unlink(file).catch(console.log);
}
}
}
const ABYTES = 17;
export async function exists(filename) {
let path = `${cacheDir}/${filename}`;
@@ -216,9 +186,6 @@ export async function exists(filename) {
);
if (stat.size !== expectedFileSize) {
DatabaseLogger.log(
`File size mismatch: ${filename}, expected: ${expectedFileSize}, actual: ${stat.size}`
);
RNFetchBlob.fs
.unlink(existsInAppGroup ? appGroupPath : path)
.catch(console.log);
@@ -229,29 +196,3 @@ export async function exists(filename) {
}
return exists;
}
export async function bulkExists(files) {
const cacheFiles = await RNFetchBlob.fs.ls(cacheDir);
let missingFiles = files.filter((file) => !cacheFiles.includes(file));
if (Platform.OS === "ios") {
const iosAppGroup =
Platform.OS === "ios"
? await RNFetchBlob.fs.pathForAppGroup(IOS_APPGROUPID)
: null;
const appGroupFiles = await RNFetchBlob.fs.ls(iosAppGroup);
missingFiles = missingFiles.filter((file) => !appGroupFiles.includes(file));
}
return missingFiles;
}
export async function getCacheSize() {
const stat = await RNFetchBlob.fs.lstat(`file://` + cacheDir);
let total = 0;
console.log("Total files", stat.length);
stat.forEach((s) => {
total += parseInt(s.size);
});
return total;
}

View File

@@ -17,47 +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 { Platform } from "react-native";
import RNFetchBlob from "react-native-blob-util";
import { ToastManager } from "../../services/event-manager";
import { useAttachmentStore } from "../../stores/use-attachment-store";
import { IOS_APPGROUPID } from "../../utils/constants";
import { DatabaseLogger, db } from "../database";
import { cacheDir } from "./utils";
import { isImage, isDocument } from "@notesnook/core/dist/utils/filename";
import { Platform } from "react-native";
import { IOS_APPGROUPID } from "../../utils/constants";
import { createCacheDir } from "./io";
import { cacheDir, getUploadedFileSize } from "./utils";
export async function uploadFile(filename, requestOptions, cancelToken) {
if (!requestOptions) return false;
let { url, headers } = requestOptions;
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 {
const uploadedFileSize = await getUploadedFileSize(filename);
if (uploadedFileSize === -1) {
const error = `Uploaded file verification failed. (File hash: ${filename})`;
throw new Error(error);
}
if (uploadedFileSize !== 0) {
DatabaseLogger.log(`File ${filename} is already uploaded.`);
return true;
}
let res = await fetch(url, {
method: "PUT",
headers
});
const uploadUrl = res.ok ? await res.text() : await res.json();
if (typeof uploadUrl !== "string") {
throw new Error(
uploadUrl.error || "Unable to resolve attachment upload url."
);
}
if (!res.ok) throw new Error(`${res.status}: Unable to resolve upload url`);
const uploadUrl = await res.text();
if (!uploadUrl) throw new Error("Unable to resolve attachment upload url");
let uploadFilePath = `${cacheDir}/${filename}`;
const iosAppGroup =
@@ -68,15 +50,7 @@ export async function uploadFile(filename, requestOptions, cancelToken) {
let exists = await RNFetchBlob.fs.exists(uploadFilePath);
if (!exists && Platform.OS === "ios") {
uploadFilePath = appGroupPath;
exists = await RNFetchBlob.fs.exists(uploadFilePath);
}
if (!exists) {
throw new Error(
`Trying to upload file at path ${uploadFilePath} that doest not exist.`
);
}
DatabaseLogger.info(`Starting upload: ${filename}`);
let request = RNFetchBlob.config({
@@ -115,6 +89,9 @@ export async function uploadFile(filename, requestOptions, cancelToken) {
);
let attachment = await db.attachments.attachment(filename);
if (!attachment) return result;
if (!isImage(attachment.mimeType) && !isDocument(attachment.mimeType)) {
RNFetchBlob.fs.unlink(`${cacheDir}/${filename}`).catch(console.log);
}
} else {
const fileInfo = await RNFetchBlob.fs.stat(uploadFilePath);
throw new Error(
@@ -127,10 +104,8 @@ export async function uploadFile(filename, requestOptions, cancelToken) {
return result;
} catch (e) {
useAttachmentStore.getState().remove(filename);
ToastManager.error(e, "File upload failed");
DatabaseLogger.error(e, "File upload failed", {
filename
});
DatabaseLogger.info(`File upload error: ${filename}, ${e}`);
DatabaseLogger.error(e);
return false;
}
}

View File

@@ -17,13 +17,10 @@ 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 * as ScopedStorage from "react-native-scoped-storage";
import { Platform } from "react-native";
import RNFetchBlob from "react-native-blob-util";
import * as ScopedStorage from "react-native-scoped-storage";
import { DatabaseLogger, db } from "../database";
import { hosts } from "@notesnook/core";
export const ABYTES = 17;
export const cacheDirOld = RNFetchBlob.fs.dirs.CacheDir;
export const cacheDir =
@@ -37,36 +34,24 @@ export function getRandomId(prefix) {
.replace("0.", prefix || "");
}
/**
*
* @param {string | undefined} data
* @returns
*/
export function parseS3Error(data) {
const xml = typeof data === "string" ? data : null;
export function extractValueFromXmlTag(code, xml) {
if (!xml.includes(code)) return `Unknown ${code}`;
return xml.slice(
xml.indexOf(`<${code}>`) + code.length + 2,
xml.indexOf(`</${code}>`)
);
}
const error = {
Code: "UNKNOWN",
Message: xml
};
try {
if (!xml) return error;
const doc = new DOMParser().parseFromString(xml, "text/xml");
const ErrorElement = doc.getElementsByTagName("Error")[0];
if (!ErrorElement) return error;
for (const child of ErrorElement.children) {
if (
child.textContent &&
(child.tagName === "Code" || child.tagName === "Message")
)
error[child.tagName] = child.textContent;
export async function fileCheck(response, totalSize) {
if (totalSize < 1000) {
let text = await response.text();
if (text.startsWith("<?xml")) {
let errorJson = {
Code: extractValueFromXmlTag("Code", text),
Message: extractValueFromXmlTag("Message", text)
};
throw new Error(`${errorJson.Code}: ${errorJson.Message}`);
}
return error;
} catch (e) {
return error;
}
}
@@ -74,11 +59,12 @@ export function cancelable(operation) {
const cancelToken = {
cancel: () => {}
};
return (filename, requestOptions) => {
return (filename, { url, headers }) => {
return {
execute: () => operation(filename, requestOptions, cancelToken),
execute: () => operation(filename, { url, headers }, cancelToken),
cancel: async () => {
await cancelToken.cancel();
RNFetchBlob.fs.unlink(`${cacheDir}/${filename}`).catch(console.log);
}
};
};
@@ -105,21 +91,3 @@ export async function releasePermissions(path) {
}
}
}
export async function getUploadedFileSize(hash) {
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 contentLength = parseInt(
attachmentInfo.headers?.get("content-length")
);
return isNaN(contentLength) ? 0 : contentLength;
} catch (e) {
DatabaseLogger.error(e);
return -1;
}
}

View File

@@ -20,13 +20,15 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
import React from "react";
import { Linking, View } from "react-native";
//import SettingsBackupAndRestore from '../../screens/settings/backup-restore';
import { useThemeColors } from "@notesnook/theme";
import { eSendEvent, presentSheet } from "../../services/event-manager";
import { eCloseAnnouncementDialog } from "../../utils/events";
import Sync from "../../services/sync";
import { useThemeColors } from "@notesnook/theme";
import { eCloseAnnouncementDialog, eCloseSheet } from "../../utils/events";
import { SIZE } from "../../utils/size";
import { sleep } from "../../utils/time";
import { PricingPlans } from "../premium/pricing-plans";
import SheetProvider from "../sheet-provider";
import { Progress } from "../sheets/progress";
import { Button } from "../ui/button";
import { allowedOnPlatform, getStyle } from "./functions";
@@ -54,6 +56,13 @@ export const Cta = ({ actions, style = {}, color, inline }) => {
/>
)
});
} else if (item.type === "force-sync") {
eSendEvent(eCloseSheet);
await sleep(300);
Progress.present();
Sync.run("global", true, true, () => {
eSendEvent(eCloseSheet);
});
}
};
return (

View File

@@ -18,15 +18,10 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import React, { useCallback, useEffect, useRef } from "react";
import {
AppStateStatus,
Platform,
TextInput,
useWindowDimensions,
View
} from "react-native";
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,
@@ -39,20 +34,17 @@ import {
import { MMKV } from "../../common/database/mmkv";
import { useAppState } from "../../hooks/use-app-state";
import BiometricService from "../../services/biometrics";
import { ToastManager } from "../../services/event-manager";
import SettingsService from "../../services/settings";
import { useSettingStore } from "../../stores/use-setting-store";
import { useUserStore } from "../../stores/use-user-store";
import { NotesnookModule } from "../../utils/notesnook-module";
import { SIZE } from "../../utils/size";
import { Toast } from "../toast";
import { Button } from "../ui/button";
import { IconButton } from "../ui/icon-button";
import Input from "../ui/input";
import Seperator from "../ui/seperator";
import Heading from "../ui/typography/heading";
import Paragraph from "../ui/typography/paragraph";
import { KeyboardAwareScrollView } from "react-native-keyboard-aware-scroll-view";
const getUser = () => {
const user = MMKV.getString("user");
@@ -82,7 +74,6 @@ const verifyUserPassword = async (password: string) => {
};
const AppLockedOverlay = () => {
const initialLaunchBiometricRequest = useRef(true);
const { colors } = useThemeColors();
const user = getUser();
const appLocked = useUserStore((state) => state.appLocked);
@@ -91,9 +82,7 @@ const AppLockedOverlay = () => {
const passwordInputRef = useRef<TextInput>(null);
const password = useRef<string>();
const appState = useAppState();
const lastAppState = useRef<AppStateStatus>(appState);
const biometricUnlockAwaitingUserInput = useRef(false);
const { height } = useWindowDimensions();
const keyboardType = useSettingStore(
(state) => state.settings.applockKeyboardType
);
@@ -128,6 +117,7 @@ const AppLockedOverlay = () => {
);
if (unlocked) {
lockApp(false);
enabled(false);
password.current = undefined;
}
biometricUnlockAwaitingUserInput.current = false;
@@ -141,8 +131,9 @@ const AppLockedOverlay = () => {
if (!password.current) return;
try {
const unlocked = appLockHasPasswordSecurity
? await validateAppLockPassword(password.current)
? validateAppLockPassword(password.current)
: await verifyUserPassword(password.current);
if (unlocked) {
if (!appLockHasPasswordSecurity) {
await setAppLockVerificationCipher(password.current);
@@ -154,13 +145,8 @@ const AppLockedOverlay = () => {
}
lockApp(false);
enabled(false);
password.current = undefined;
} else {
ToastManager.show({
heading: `Invalid ${keyboardType === "numeric" ? "pin" : "password"}`,
type: "error",
context: "local"
});
}
} catch (e) {
console.error(e);
@@ -168,53 +154,32 @@ const AppLockedOverlay = () => {
};
useEffect(() => {
const prevState = lastAppState.current;
lastAppState.current = appState;
if (
biometricUnlockAwaitingUserInput.current ||
useUserStore.getState().disableAppLockRequests
)
return;
if (
appLocked &&
appState === "active" &&
(prevState === "background" || initialLaunchBiometricRequest.current)
!useSettingStore.getState().requestBiometrics
) {
if (SettingsService.shouldLockAppOnEnterForeground()) {
DatabaseLogger.info("Locking app on entering foreground");
useUserStore.getState().lockApp(true);
}
if (
!(
biometricUnlockAwaitingUserInput.current ||
useUserStore.getState().disableAppLockRequests ||
!appLocked ||
useSettingStore.getState().requestBiometrics ||
useSettingStore.getState().appDidEnterBackgroundForAction
)
) {
initialLaunchBiometricRequest.current = false;
DatabaseLogger.info("Biometric unlock request");
onUnlockAppRequested();
}
} else {
SettingsService.appEnteredBackground();
biometricUnlockAwaitingUserInput.current = true;
onUnlockAppRequested();
}
}, [appState, onUnlockAppRequested, appLocked]);
return appLocked ? (
<KeyboardAwareScrollView
<View
style={{
backgroundColor: colors.primary.background,
width: "100%",
height: "100%",
position: "absolute",
zIndex: 999
zIndex: 999,
justifyContent: "center"
}}
contentContainerStyle={{
justifyContent: "center",
minHeight: height
}}
keyboardDismissMode="interactive"
keyboardShouldPersistTaps="handled"
>
<Toast context="local" />
<View
style={{
flex: 1,
@@ -327,7 +292,7 @@ const AppLockedOverlay = () => {
</View>
</View>
</View>
</KeyboardAwareScrollView>
</View>
) : null;
};

View File

@@ -30,8 +30,6 @@ import filesystem from "../../common/filesystem";
import downloadAttachment from "../../common/filesystem/download-attachment";
import { useAttachmentProgress } from "../../hooks/use-attachment-progress";
import picker from "../../screens/editor/tiptap/picker";
import { useTabStore } from "../../screens/editor/tiptap/use-tab-store";
import { editorController } from "../../screens/editor/tiptap/utils";
import {
ToastManager,
eSendEvent,
@@ -51,12 +49,13 @@ import { Dialog } from "../dialog";
import { presentDialog } from "../dialog/functions";
import { openNote } from "../list-items/note/wrapper";
import { DateMeta } from "../properties/date-meta";
import SheetProvider from "../sheet-provider";
import { Button } from "../ui/button";
import { Notice } from "../ui/notice";
import { Pressable } from "../ui/pressable";
import Heading from "../ui/typography/heading";
import Paragraph from "../ui/typography/paragraph";
import { useTabStore } from "../../screens/editor/tiptap/use-tab-store";
import { editorController } from "../../screens/editor/tiptap/utils";
const Actions = ({
attachment,
@@ -88,7 +87,7 @@ const Actions = ({
useAttachmentStore.getState().remove(attachment.hash);
}
downloadAttachment(attachment.hash, false);
fwdRef.current?.hide();
eSendEvent(eCloseSheet, contextId);
},
icon: "download"
},
@@ -118,14 +117,12 @@ const Actions = ({
setLoading({
name: "Run file check"
});
const result = await filesystem.checkAttachment(attachment.hash);
if (!result) return;
if (result.failed) {
db.attachments.markAsFailed(attachment.id, result.failed);
setFailed(result.failed);
const res = await filesystem.checkAttachment(attachment.hash);
if (res.failed) {
db.attachments.markAsFailed(attachment.id, res.failed);
setFailed(res.failed);
ToastManager.show({
heading: "File check failed with error: " + result.failed,
heading: "File check failed with error: " + res.failed,
type: "error",
context: "local"
});
@@ -149,8 +146,9 @@ const Actions = ({
},
{
name: "Rename",
onPress: async () => {
onPress: () => {
presentDialog({
context: contextId as any,
input: true,
title: "Rename file",
paragraph: "Enter a new name for the file",
@@ -218,14 +216,12 @@ const Actions = ({
style={{
maxHeight: "100%"
}}
keyboardShouldPersistTaps="never"
>
<Dialog context={contextId} />
<SheetProvider context={contextId} />
<View
style={{
borderBottomWidth: 1,
borderBottomColor: colors.primary.border,
borderBottomColor: colors.secondary.background,
marginBottom: notes && notes.length > 0 ? 0 : 12
}}
>
@@ -242,20 +238,36 @@ const Actions = ({
style={{
flexDirection: "row",
marginBottom: 10,
paddingHorizontal: 12,
marginTop: 6,
gap: 10
paddingHorizontal: 12
}}
>
<Paragraph size={SIZE.xs} color={colors.secondary.paragraph}>
{attachment.mimeType}
<Paragraph
size={SIZE.xs}
style={{
marginRight: 10
}}
color={colors.secondary.paragraph}
>
{attachment.type}
</Paragraph>
<Paragraph size={SIZE.xs} color={colors.secondary.paragraph}>
<Paragraph
style={{
marginRight: 10
}}
size={SIZE.xs}
color={colors.secondary.paragraph}
>
{formatBytes(attachment.size)}
</Paragraph>
{notes.length ? (
<Paragraph size={SIZE.xs} color={colors.secondary.paragraph}>
<Paragraph
style={{
marginRight: 10
}}
size={SIZE.xs}
color={colors.secondary.paragraph}
>
{notes.length} note
{notes.length > 1 ? "s" : ""}
</Paragraph>
@@ -283,7 +295,7 @@ const Actions = ({
<View
style={{
borderBottomWidth: 1,
borderBottomColor: colors.primary.border,
borderBottomColor: colors.secondary.background,
marginBottom: 12,
paddingVertical: 12
}}
@@ -354,7 +366,7 @@ const Actions = ({
{failed ? (
<Notice
type="alert"
text={`File check failed: ${failed} Try reuploading the file to fix the issue.`}
text={`File check failed with error: ${attachment.failed} Try reuploading the file to fix the issue.`}
size="small"
/>
) : null}

View File

@@ -22,6 +22,7 @@ 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";
@@ -43,8 +44,7 @@ export const AttachmentItem = ({
setAttachments,
pressable = true,
hideWhenNotDownloading,
context,
errorOnly
context
}: {
id: string | number;
attachments?: VirtualizedGrouping<Attachment>;
@@ -53,9 +53,9 @@ export const AttachmentItem = ({
pressable?: boolean;
hideWhenNotDownloading?: boolean;
context?: string;
errorOnly?: boolean;
}) => {
const [attachment] = useDBItem(id, "attachment", attachments);
const { colors } = useThemeColors();
const [currentProgress, setCurrentProgress] = useAttachmentProgress(
attachment,
@@ -67,7 +67,8 @@ export const AttachmentItem = ({
Actions.present(attachment, setAttachments, context);
};
return errorOnly && attachment && !attachment?.failed ? null : (
return hideWhenNotDownloading &&
(!currentProgress || !currentProgress.value) ? null : (
<TouchableOpacity
activeOpacity={0.9}
onPress={onPress}
@@ -77,6 +78,8 @@ export const AttachmentItem = ({
justifyContent: "space-between",
padding: 12,
paddingVertical: 6,
borderRadius: 5,
backgroundColor: colors.secondary.background,
minHeight: 45
}}
>
@@ -86,46 +89,41 @@ export const AttachmentItem = ({
style={{
flexShrink: 1,
flexDirection: "row",
alignItems: "center",
gap: 10
alignItems: "center"
}}
>
<View
style={{
justifyContent: "center",
alignItems: "center",
marginLeft: -5,
borderWidth: 1,
borderColor: colors.secondary.border,
paddingHorizontal: 2,
width: 30,
height: 30,
borderRadius: 5
marginLeft: -5
}}
>
<Icon name="file" size={SIZE.xxxl} color={colors.primary.icon} />
<Paragraph
adjustsFontSizeToFit
size={8}
color={colors.secondary.paragraph}
size={6}
color={colors.static.white}
style={{
maxWidth: 30
position: "absolute"
}}
numberOfLines={1}
>
{getFileExtension(attachment.filename).toUpperCase() ||
attachment.mimeType.split("/")?.[1]?.toUpperCase()}
{getFileExtension(attachment.filename).toUpperCase()}
</Paragraph>
</View>
<View
style={{
flexShrink: 1
flexShrink: 1,
marginLeft: 10
}}
>
<Paragraph
size={SIZE.sm}
size={SIZE.sm - 1}
style={{
flexWrap: "wrap"
flexWrap: "wrap",
marginBottom: 2.5
}}
numberOfLines={1}
lineBreakMode="middle"
@@ -135,8 +133,11 @@ export const AttachmentItem = ({
</Paragraph>
{!hideWhenNotDownloading ? (
<Paragraph color={colors.secondary.paragraph} size={SIZE.xxs}>
File size: {formatBytes(attachment.size)}
<Paragraph color={colors.secondary.paragraph} size={SIZE.xs}>
{formatBytes(attachment.size)}{" "}
{currentProgress?.type
? "(" + currentProgress.type + "ing - tap to cancel)"
: ""}
</Paragraph>
) : null}
</View>

View File

@@ -0,0 +1,296 @@
/*
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, { useRef, useState } from "react";
import { Platform, View } from "react-native";
import { db } from "../../common/database";
import { downloadAttachments } from "../../common/filesystem/download-attachment";
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 { 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
}: {
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,
statusText: "Download started... Please wait"
});
const [result, setResult] = useState(new Map());
const canceled = useRef(false);
const groupId = useRef<string>();
const onDownload = async () => {
update?.({
disableClosing: true
} as PresentSheetOptions);
setDownloading(true);
canceled.current = false;
groupId.current = Date.now().toString();
const result = await downloadAttachments(
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?.({
disableClosing: false
} as PresentSheetOptions);
};
const cancel = async () => {
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, "download");
setDownloading(false);
groupId.current = undefined;
};
const failedResults = () => {
const results = [];
for (const value of result.values()) {
if (value.status === 0) results.push(value.attachment);
}
return results;
};
function getResultText() {
const downloadedAttachmentsCount =
attachments?.placeholders?.length - failedResults().length;
if (downloadedAttachmentsCount === 0)
return "Failed to download all attachments";
return `Successfully downloaded ${downloadedAttachmentsCount}/${
attachments?.placeholders.length
} attachments as a zip file at ${
Platform.OS === "android" ? "the selected folder" : "Notesnook/downloads"
}`;
}
return (
<View
style={{
alignItems: "center",
width: "100%",
paddingVertical: 12,
paddingHorizontal: 12
}}
>
<Heading>
{downloading
? "Downloading attachments"
: result?.size
? "Downloaded attachments"
: "Download attachments"}
</Heading>
{downloading ? (
<Paragraph
style={{
textAlign: "center"
}}
>
{progress.statusText}
</Paragraph>
) : result?.size ? (
<Paragraph
style={{
textAlign: "center"
}}
>
{getResultText()}
</Paragraph>
) : (
<Paragraph
style={{
textAlign: "center"
}}
>
Are you sure you want to download all attachments
{isNote ? " of this note?" : "?"}
</Paragraph>
)}
{downloading ? (
<View
style={{
width: 200,
marginTop: 10
}}
>
<ProgressBarComponent
height={5}
width={null}
animated={true}
useNativeDriver
progress={
progress.value
? progress.value / attachments.placeholders?.length
: 0
}
unfilledColor={colors.secondary.background}
color={colors.primary.accent}
borderWidth={0}
/>
</View>
) : null}
<FlatList
style={{
maxHeight: 300,
width: "100%",
minHeight: 60,
backgroundColor: colors.secondary.background,
borderRadius: 5,
marginVertical: 12
}}
data={downloading ? attachments.placeholders : undefined}
ListEmptyComponent={
<View
style={{
width: "100%",
justifyContent: "center",
alignItems: "center",
height: 60
}}
>
<Paragraph color={colors.secondary.paragraph}>
No downloads in progress.
</Paragraph>
</View>
}
keyExtractor={(index) => "attachment" + index}
renderItem={({ index }) => {
return (
<AttachmentItem
id={index}
setAttachments={() => {}}
pressable={false}
hideWhenNotDownloading={true}
attachments={attachments}
/>
);
}}
/>
{result?.size ? (
<Button
style={{
width: 250,
borderRadius: 100,
marginTop: 20
}}
onPress={() => {
close?.();
}}
type="accent"
title="Done"
/>
) : !downloading ? (
<View
style={{
flexDirection: "row",
width: "100%",
marginTop: 20
}}
>
<Button
style={{
flex: 1,
borderRadius: 100,
marginRight: 5
}}
onPress={() => {
close?.();
}}
type="secondary"
title="No"
/>
<Button
style={{
flex: 1,
borderRadius: 100,
marginLeft: 5
}}
onPress={onDownload}
type="accent"
title="Yes"
/>
</View>
) : (
<Button
style={{
width: 250,
borderRadius: 100,
marginTop: 20
}}
onPress={cancel}
type="error"
title="Cancel"
/>
)}
</View>
);
};
DownloadAttachments.present = (
context: string,
attachments: VirtualizedGrouping<Attachment>,
isNote?: boolean
) => {
presentSheet({
context: context,
component: (ref, close, update) => (
<DownloadAttachments
close={close}
attachments={attachments}
isNote={isNote}
update={update}
/>
)
});
};
export default DownloadAttachments;

View File

@@ -19,28 +19,21 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
import {
Attachment,
FilteredSelector,
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, View } from "react-native";
import { ActivityIndicator, ScrollView, View } from "react-native";
import { FlashList } from "react-native-actions-sheet/dist/src/views/FlashList";
import { ScrollView } from "react-native-gesture-handler";
import Icon from "react-native-vector-icons/MaterialCommunityIcons";
import create, { State } from "zustand";
import { db } from "../../common/database";
import filesystem from "../../common/filesystem";
import { downloadAttachments } from "../../common/filesystem/download-attachment";
import { AttachmentGroupProgress } from "../../screens/settings/attachment-group-progress";
import { presentSheet, ToastManager } from "../../services/event-manager";
import { useAttachmentStore } from "../../stores/use-attachment-store";
import { presentSheet } from "../../services/event-manager";
import { useSettingStore } from "../../stores/use-setting-store";
import { SIZE } from "../../utils/size";
import { Dialog } from "../dialog";
import { presentDialog } from "../dialog/functions";
import { Header } from "../header";
import SheetProvider from "../sheet-provider";
import { Button } from "../ui/button";
import { IconButton } from "../ui/icon-button";
@@ -49,128 +42,36 @@ import Seperator from "../ui/seperator";
import Heading from "../ui/typography/heading";
import Paragraph from "../ui/typography/paragraph";
import { AttachmentItem } from "./attachment-item";
import DownloadAttachments from "./download-attachments";
const DEFAULT_SORTING: SortOptions = {
sortBy: "dateEdited",
sortDirection: "desc"
};
type RecheckerProgress = {
failed: number;
passed: number;
isWorking: boolean;
shown: boolean;
filter: string;
};
interface RecheckerState extends State {
progress: {
[key: string]: RecheckerProgress;
};
setProgress: (key: string, progress: RecheckerProgress) => void;
currentFilter: string;
}
const useRechecker = create<RecheckerState>((set) => ({
progress: {
all: {
failed: 0,
passed: 0,
isWorking: false,
shown: false,
filter: "all"
}
},
currentFilter: "all",
setProgress: (key: string, progress: RecheckerProgress) => {
set((state) => ({
progress: {
...state.progress,
[key]: progress
}
}));
}
}));
const attachmentTypes = [
{
title: "All",
filterBy: "all"
},
{
title: "Images",
filterBy: "images"
},
{
title: "Docs",
filterBy: "documents"
},
{
title: "Video",
filterBy: "video"
},
{
title: "Audio",
filterBy: "audio"
},
{
title: "Orphaned",
filterBy: "orphaned"
},
{
title: "Errors",
filterBy: "errors"
}
];
export const AttachmentDialog = ({
note,
isSheet
}: {
note?: Note;
isSheet: boolean;
}) => {
export const AttachmentDialog = ({ note }: { note?: Note }) => {
const { colors } = useThemeColors();
const { height } = useSettingStore((state) => state.dimensions);
const [attachments, setAttachments] =
useState<VirtualizedGrouping<Attachment>>();
const attachmentSearchValue = useRef<string>();
const [loading, setLoading] = useState(true);
const searchTimer = useRef<NodeJS.Timeout>();
const [loading, setLoading] = useState(false);
const [currentFilter, setCurrentFilter] = useState("all");
const rechecker = useRechecker((state) =>
note ? state.progress[note.id] || {} : state.progress.all
);
const currentFilterRef = useRef(currentFilter);
currentFilterRef.current = currentFilter;
useEffect(() => {
useRechecker.setState({
currentFilter: currentFilter
});
}, [currentFilter]);
const refresh = React.useCallback(() => {
if (note) {
db.attachments
.ofNote(note.id, "all")
.sorted({
...DEFAULT_SORTING,
sortBy: "dateModified"
})
.then((attachments) => {
setLoading(false);
setAttachments(attachments);
});
db.attachments.ofNote(note.id, "all").sorted({
...DEFAULT_SORTING,
sortBy: "dateModified"
});
} else {
db.attachments.all
.sorted({
...DEFAULT_SORTING,
sortBy: "dateModified"
})
.then((attachments) => {
setAttachments(attachments);
setLoading(false);
});
.then((attachments) => setAttachments(attachments));
}
}, [note]);
@@ -202,79 +103,59 @@ export const AttachmentDialog = ({
setAttachments={async () => {
setAttachments(await filterAttachments(currentFilter));
}}
errorOnly={currentFilter === "errors"}
attachments={attachments}
id={index}
context="global"
context="attachments-list"
/>
);
const onCheck = async () => {
const getState = () =>
useRechecker.getState().progress[note?.id || "all"] || {};
const setState = (state: Partial<RecheckerProgress>) =>
useRechecker.getState().setProgress(note?.id || "all", {
...getState(),
...state
});
if (!attachments) return;
setLoading(true);
if (!attachments || getState().isWorking) return;
setState({
isWorking: true,
failed: 0,
passed: 0,
shown: true,
filter: currentFilterRef.current
});
const filter = currentFilterRef.current;
const filteredAttachments = await filterAttachments(
currentFilterRef.current
);
for (let i = 0; i < filteredAttachments.placeholders.length; i++) {
if (!getState().isWorking) {
ToastManager.show({
message: "Attachment recheck cancelled",
type: "info",
context: isSheet ? "local" : "global"
});
return;
}
for (let i = 0; i < attachments.placeholders.length; i++) {
const attachment = (await attachments.item(i))?.item;
if (currentFilter == "errors" && !attachment?.failed) continue;
if (!attachment) continue;
const result = await filesystem.checkAttachment(attachment.hash);
if (!result) return;
if (result.failed) {
setState({
failed: getState().failed + 1
});
await db.attachments.markAsFailed(attachment.hash, result.failed);
} else {
setState({
passed: getState().passed + 1
});
await db.attachments.markAsFailed(attachment.id);
await db.attachments.markAsFailed(attachment.id, undefined);
}
}
if (filter === useRechecker.getState().currentFilter) {
setAttachments(await filterAttachments(currentFilter));
}
setState({
isWorking: false
});
refresh();
setLoading(false);
};
const attachmentTypes = [
{
title: "All",
filterBy: "all"
},
{
title: "Images",
filterBy: "images"
},
{
title: "Documents",
filterBy: "documents"
},
{
title: "Video",
filterBy: "video"
},
{
title: "Audio",
filterBy: "audio"
}
];
const filterAttachments = async (type: string) => {
let items: FilteredSelector<Attachment> = db.attachments.all;
switch (type) {
case "all":
items = note
? db.attachments.ofNote(note.id, "all")
: db.attachments.all;
items = db.attachments.all;
break;
case "images":
items = note
@@ -283,26 +164,16 @@ export const AttachmentDialog = ({
break;
case "video":
items = items = note
? db.attachments.ofNote(note.id, "videos")
? db.attachments.ofNote(note.id, "all")
: db.attachments.videos;
break;
case "audio":
items = note
? db.attachments.ofNote(note.id, "audio")
: db.attachments.audios;
items = db.attachments.all;
break;
case "documents":
items = note
? db.attachments.ofNote(note.id, "documents")
: db.attachments.documents;
break;
case "orphaned":
items = db.attachments.orphaned;
break;
case "errors":
items = items = note
? db.attachments.ofNote(note.id, "all")
: db.attachments.all;
: db.attachments.documents;
}
return await items.sorted({
@@ -312,59 +183,39 @@ export const AttachmentDialog = ({
};
return (
<>
{isSheet ? (
<>
<SheetProvider context="attachments-list" />
<Dialog context="local" />
</>
) : null}
{!isSheet ? (
<Header
title="Manage attachments"
renderedInRoute="SettingsGroup"
canGoBack
headerRightButtons={[
{
onPress() {
onCheck();
},
title: "Recheck all"
},
{
onPress() {
if (!attachments) return;
presentDialog({
title: `Download ${attachments.placeholders.length} attachments`,
paragraph:
"Are you sure you want to download all attachments?",
positiveText: "Download",
positivePress: async () => {
downloadAttachments(await attachments.ids());
},
negativeText: "Cancel"
});
},
title: "Download all"
}
]}
/>
) : (
<View
style={{
width: "100%",
alignSelf: "center",
paddingHorizontal: 12,
height: height * 0.85
}}
>
<SheetProvider context="attachments-list" />
<View
style={{
flexDirection: "row",
justifyContent: "space-between",
alignItems: "center"
}}
>
<Heading>Attachments</Heading>
<View
style={{
flexDirection: "row",
justifyContent: "space-between",
alignItems: "center",
paddingHorizontal: 12
flexDirection: "row"
}}
>
<Heading>Attachments</Heading>
<View
style={{
flexDirection: "row"
}}
>
{loading ? (
<ActivityIndicator
style={{
height: 40,
width: 40,
marginRight: 10
}}
size={SIZE.lg}
/>
) : (
<IconButton
name="check-all"
style={{
@@ -376,243 +227,125 @@ export const AttachmentDialog = ({
size={SIZE.lg}
onPress={onCheck}
/>
)}
<IconButton
name="download"
style={{
height: 40,
width: 40
}}
color={colors.primary.paragraph}
onPress={() => {
if (!attachments) return;
presentDialog({
title: `Download ${attachments.placeholders.length} attachments`,
paragraph:
"Are you sure you want to download all attachments?",
context: "local",
positiveText: "Download",
positivePress: async () => {
downloadAttachments(await attachments.ids());
},
negativeText: "Cancel"
});
}}
size={SIZE.lg}
/>
</View>
<IconButton
name="download"
style={{
height: 40,
width: 40
}}
color={colors.primary.paragraph}
onPress={() => {
if (!attachments) return;
DownloadAttachments.present(
"attachments-list",
attachments,
!!note
);
}}
size={SIZE.lg}
/>
</View>
)}
</View>
<View
style={{
width: "100%",
alignSelf: "center",
paddingHorizontal: 12,
height: "100%"
<Seperator />
<Input
placeholder="Filter attachments by filename, type or hash"
onChangeText={onChangeText}
onSubmit={() => {
onChangeText(attachmentSearchValue.current as string);
}}
>
<Seperator />
<Input
placeholder="Filter attachments by filename, type or hash"
onChangeText={onChangeText}
onSubmit={() => {
onChangeText(attachmentSearchValue.current as string);
}}
/>
/>
{rechecker.shown ? (
<View>
<ScrollView
style={{
width: "100%",
height: 50,
flexDirection: "row",
backgroundColor: colors.primary.background
}}
contentContainerStyle={{
minWidth: "100%",
height: 50
}}
horizontal
>
{attachmentTypes.map((item) => (
<Button
type={
currentFilter === item.filterBy ? "secondaryAccented" : "plain"
}
key={item.title}
title={item.title}
style={{
borderRadius: 0,
borderBottomWidth: 1,
flexGrow: 1,
borderBottomColor:
currentFilter !== item.filterBy
? "transparent"
: colors.primary.accent
}}
onPress={async () => {
setCurrentFilter(item.filterBy);
setAttachments(await filterAttachments(item.filterBy));
}}
/>
))}
</ScrollView>
</View>
<FlashList
keyboardDismissMode="none"
keyboardShouldPersistTaps="always"
ListEmptyComponent={
<View
style={{
flexDirection: "row",
alignItems: "center",
width: "100%",
borderRadius: 10,
padding: 10,
borderWidth: 1,
borderColor: colors.primary.border,
gap: 12,
justifyContent: "space-between"
}}
>
<View
style={{
flexDirection: "row",
gap: 12,
alignItems: "center"
}}
>
{rechecker.isWorking ? (
<ActivityIndicator color={colors.primary.accent} />
) : (
<Icon
name="file-check"
size={30}
color={colors.primary.accent}
/>
)}
<View>
<Paragraph>
{rechecker.isWorking
? note
? `Checking ${rechecker.filter.toLowerCase()} note attachments`
: `Checking ${rechecker.filter.toLowerCase()} attachments`
: "Attachments recheck complete"}
</Paragraph>
<Paragraph>
{`${rechecker.isWorking ? "Please wait... " : ""}Passed: ${
rechecker.passed
}, Failed: ${rechecker.failed}`}
</Paragraph>
</View>
</View>
<IconButton
type={rechecker.isWorking ? "errorShade" : "plain"}
name={rechecker.isWorking ? "close" : "check"}
size={SIZE.lg}
color={
rechecker.isWorking ? colors.error.icon : colors.primary.accent
}
onPress={() => {
useRechecker.getState().setProgress(note?.id || "all", {
...useRechecker.getState().progress[note?.id || "all"],
shown: false,
isWorking: false
});
}}
/>
</View>
) : null}
<View>
<ScrollView
style={{
backgroundColor: colors.primary.background,
flexWrap: "wrap",
flexDirection: "row",
paddingVertical: 12
}}
contentContainerStyle={{
height: 150,
justifyContent: "center",
alignItems: "center"
}}
horizontal
>
{attachmentTypes.map((item) =>
item.filterBy === "orphaned" && note ? null : (
<Button
type={
currentFilter === item.filterBy
? "secondaryAccented"
: "plain"
}
key={item.title}
title={item.title}
fontSize={SIZE.sm}
style={{
borderRadius: 100,
paddingHorizontal: 12,
height: 40,
minWidth: 80
}}
onPress={async () => {
const filterBy = item.filterBy;
setCurrentFilter(filterBy);
setLoading(true);
filterAttachments(filterBy)
.then((results) => {
if (filterBy !== currentFilterRef.current) return;
setAttachments(results);
setLoading(false);
})
.catch(console.log);
}}
/>
)
)}
</ScrollView>
</View>
<FlashList
keyboardDismissMode="none"
keyboardShouldPersistTaps="always"
ListEmptyComponent={
<View
style={{
height: "100%",
justifyContent: "center",
alignItems: "center"
}}
>
{loading ? (
<ActivityIndicator size={40} color={colors.primary.accent} />
) : (
<>
<Icon
name="attachment"
size={60}
color={colors.secondary.icon}
/>
<Paragraph>
{note ? "No attachments on this note" : "No attachments"}
</Paragraph>
</>
)}
</View>
}
ListHeaderComponent={<AllProgress note={note} />}
ListFooterComponent={
<View
style={{
height: 350
}}
/>
}
estimatedItemSize={50}
data={loading ? [] : attachments?.placeholders}
renderItem={renderItem}
/>
</View>
</>
);
};
const AllProgress = ({ note }: { note?: Note }) => {
const progress = useAttachmentStore((state) =>
note
? {
downloading: {
[note.id]: state.downloading?.[note.id]
},
uploading: {
[note.id]: state.uploading?.[note.id]
}
<Icon name="attachment" size={60} color={colors.secondary.icon} />
<Paragraph>
{note ? "No attachments on this note" : "No attachments"}
</Paragraph>
</View>
}
: state
);
ListFooterComponent={
<View
style={{
height: 350
}}
/>
}
estimatedItemSize={50}
data={attachments?.placeholders}
renderItem={renderItem}
/>
return (
<View
style={{
gap: 10,
width: "100%"
}}
>
{Object.keys(progress.downloading || {}).map((groupId) => (
<AttachmentGroupProgress key={groupId} groupId={groupId} />
))}
{Object.keys(progress.uploading || {}).map((groupId) => (
<AttachmentGroupProgress key={groupId} groupId={groupId} />
))}
<Paragraph
color={colors.secondary.paragraph}
size={SIZE.xs}
style={{
textAlign: "center",
marginTop: 10
}}
>
<Icon
name="shield-key-outline"
size={SIZE.xs}
color={colors.primary.icon}
/>
{" "}All attachments are end-to-end encrypted.
</Paragraph>
</View>
);
};
AttachmentDialog.present = (note?: Note) => {
presentSheet({
component: () => <AttachmentDialog note={note} isSheet={true} />,
keyboardHandlerDisabled: true
component: () => <AttachmentDialog note={note} />
});
};

View File

@@ -19,6 +19,9 @@ 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
@@ -28,7 +31,9 @@ import { eCloseLoginDialog, eOpenLoginDialog } from "../../utils/events";
import { sleep } from "../../utils/time";
import BaseDialog from "../dialog/base-dialog";
import { Toast } from "../toast";
import { initialAuthMode } from "./common";
import { Button } from "../ui/button";
import { IconButton } from "../ui/icon-button";
import { hideAuth, initialAuthMode } from "./common";
import { Login } from "./login";
import { Signup } from "./signup";
@@ -44,6 +49,7 @@ const AuthModal = () => {
const [visible, setVisible] = useState(false);
const [currentAuthMode, setCurrentAuthMode] = useState(AuthMode.login);
const actionSheetRef = useRef();
const insets = useGlobalSafeAreaInsets();
useEffect(() => {
eSubscribeEvent(eOpenLoginDialog, open);
@@ -91,18 +97,81 @@ const AuthModal = () => {
centered={false}
enableSheetKeyboardHandler
>
{currentAuthMode !== AuthMode.login ? (
<Signup
changeMode={(mode) => setCurrentAuthMode(mode)}
trial={AuthMode.trialSignup === currentAuthMode}
welcome={initialAuthMode.current === AuthMode.welcomeSignup}
/>
) : (
<Login
welcome={initialAuthMode.current === AuthMode.welcomeSignup}
changeMode={(mode) => setCurrentAuthMode(mode)}
/>
)}
<KeyboardAwareScrollView
style={{
width: "100%"
}}
enableAutomaticScroll={false}
keyboardShouldPersistTaps="handled"
>
{currentAuthMode !== AuthMode.login ? (
<Signup
changeMode={(mode) => setCurrentAuthMode(mode)}
trial={AuthMode.trialSignup === currentAuthMode}
welcome={initialAuthMode.current === AuthMode.welcomeSignup}
/>
) : (
<Login
welcome={initialAuthMode.current === AuthMode.welcomeSignup}
changeMode={(mode) => setCurrentAuthMode(mode)}
/>
)}
</KeyboardAwareScrollView>
<View
style={{
position: "absolute",
paddingTop: Platform.OS === "android" ? 0 : insets.top,
top: 0,
zIndex: 999,
backgroundColor: colors.secondary.background,
width: "100%"
}}
>
<View
style={{
flexDirection: "row",
alignItems: "center",
paddingHorizontal: 12,
width: "100%",
height: 50,
justifyContent:
initialAuthMode.current !== AuthMode.welcomeSignup
? "space-between"
: "flex-end"
}}
>
{initialAuthMode.current === AuthMode.welcomeSignup ? null : (
<IconButton
name="arrow-left"
onPress={() => {
hideAuth();
}}
color={colors.primary.paragraph}
/>
)}
{initialAuthMode.current !== AuthMode.welcomeSignup ? null : (
<Button
title="Skip"
onPress={() => {
hideAuth();
}}
iconSize={16}
type="plain"
iconPosition="right"
icon="chevron-right"
height={25}
iconStyle={{
marginTop: 2
}}
style={{
paddingHorizontal: 6
}}
/>
)}
</View>
</View>
<Toast context="local" />
</BaseDialog>

View File

@@ -68,8 +68,7 @@ export const ChangePassword = () => {
setLoading(true);
try {
const result = await BackupService.run(false, "change-password-dialog");
if (result.error)
throw new Error(`Failed to create backup: ${result.error}`);
if (!result) throw new Error("Failed to create backup");
await db.user.clearSessions();
await db.user.changePassword(oldPassword.current, password.current);

View File

@@ -1,80 +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 { useThemeColors } from "@notesnook/theme";
import React from "react";
import { Platform, View } from "react-native";
import useGlobalSafeAreaInsets from "../../hooks/use-global-safe-area-insets";
import { Button } from "../ui/button";
import { IconButton } from "../ui/icon-button";
import { hideAuth } from "./common";
export const AuthHeader = (props: { welcome?: boolean }) => {
const { colors } = useThemeColors();
const insets = useGlobalSafeAreaInsets();
return (
<View
style={{
paddingTop: Platform.OS === "android" ? 0 : insets.top,
backgroundColor: colors.secondary.background,
width: "100%"
}}
>
<View
style={{
flexDirection: "row",
alignItems: "center",
paddingHorizontal: 12,
width: "100%",
height: 50,
justifyContent: !props.welcome ? "space-between" : "flex-end"
}}
>
{props.welcome ? null : (
<IconButton
name="arrow-left"
onPress={() => {
hideAuth();
}}
color={colors.primary.paragraph}
/>
)}
{!props.welcome ? null : (
<Button
title="Skip"
onPress={() => {
hideAuth();
}}
iconSize={16}
type="plain"
iconPosition="right"
icon="chevron-right"
height={25}
iconStyle={{
marginTop: 2
}}
style={{
paddingHorizontal: 6
}}
/>
)}
</View>
</View>
);
};

View File

@@ -17,17 +17,12 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { useThemeColors } from "@notesnook/theme";
import React, { useEffect, useState } from "react";
import { TouchableOpacity, View, useWindowDimensions } from "react-native";
import { SheetManager } from "react-native-actions-sheet";
import { KeyboardAwareScrollView } from "react-native-keyboard-aware-scroll-view";
import { DDS } from "../../services/device-detection";
import { eSendEvent } from "../../services/event-manager";
import Sync from "../../services/sync";
import { useSettingStore } from "../../stores/use-setting-store";
import { useUserStore } from "../../stores/use-user-store";
import { eUserLoggedIn } from "../../utils/events";
import { useThemeColors } from "@notesnook/theme";
import { SIZE } from "../../utils/size";
import { sleep } from "../../utils/time";
import SheetProvider from "../sheet-provider";
@@ -39,6 +34,10 @@ import Paragraph from "../ui/typography/paragraph";
import { hideAuth } from "./common";
import { ForgotPassword } from "./forgot-password";
import { useLogin } from "./use-login";
import { useSettingStore } from "../../stores/use-setting-store";
import { eUserLoggedIn } from "../../utils/events";
import { useUserStore } from "../../stores/use-user-store";
import Sync from "../../services/sync";
const LoginSteps = {
emailAuth: 1,
@@ -67,7 +66,7 @@ export const Login = ({ changeMode }) => {
Progress.present();
setTimeout(() => {
if (!useUserStore.getState().syncing) {
Sync.run("global", false, "full");
Sync.run("global", false, true);
}
}, 5000);
});
@@ -90,233 +89,215 @@ export const Login = ({ changeMode }) => {
<>
<ForgotPassword />
<SheetProvider context="two_factor_verify" />
<KeyboardAwareScrollView
<View
style={{
width: "100%"
borderRadius: DDS.isTab ? 5 : 0,
backgroundColor: colors.primary.background,
zIndex: 10,
width: "100%",
alignSelf: "center"
}}
contentContainerStyle={{
minHeight: "90%"
}}
nestedScrollEnabled
enableAutomaticScroll={false}
keyboardShouldPersistTaps="handled"
>
<View
style={{
borderRadius: DDS.isTab ? 5 : 0,
backgroundColor: colors.primary.background,
zIndex: 10,
width: "100%",
alignSelf: "center"
justifyContent: "flex-end",
paddingHorizontal: 20,
backgroundColor: colors.secondary.background,
borderBottomWidth: 0.8,
marginBottom: 12,
borderBottomColor: colors.primary.border,
alignSelf: deviceMode !== "mobile" ? "center" : undefined,
borderWidth: deviceMode !== "mobile" ? 1 : null,
borderColor: deviceMode !== "mobile" ? colors.primary.border : null,
borderRadius: deviceMode !== "mobile" ? 20 : null,
marginTop: deviceMode !== "mobile" ? 50 : null,
width: deviceMode === "mobile" ? null : "50%",
minHeight: height * 0.4
}}
>
<View
style={{
justifyContent: "flex-end",
paddingHorizontal: 20,
backgroundColor: colors.secondary.background,
borderBottomWidth: 0.8,
marginBottom: 12,
borderBottomColor: colors.primary.border,
alignSelf: deviceMode !== "mobile" ? "center" : undefined,
borderWidth: deviceMode !== "mobile" ? 1 : null,
borderColor:
deviceMode !== "mobile" ? colors.primary.border : null,
borderRadius: deviceMode !== "mobile" ? 20 : null,
marginTop: deviceMode !== "mobile" ? 50 : null,
width: deviceMode === "mobile" ? null : "50%",
minHeight: height * 0.4
flexDirection: "row"
}}
>
<View
style={{
flexDirection: "row"
}}
>
<View
style={{
width: 100,
height: 5,
backgroundColor: colors.primary.accent,
borderRadius: 2,
marginRight: 7
}}
/>
<View
style={{
width: 20,
height: 5,
backgroundColor: colors.secondary.background,
borderRadius: 2
}}
/>
</View>
<Heading
style={{
marginBottom: 25,
marginTop: 10
}}
extraBold
size={SIZE.xxl}
>
Login to your {"\n"}account
</Heading>
</View>
<View
style={{
width: DDS.isTab
? focused
? "50%"
: "49.99%"
: focused
? "100%"
: "99.9%",
backgroundColor: colors.primary.background,
alignSelf: "center",
paddingHorizontal: 20
}}
>
<Input
fwdRef={emailInputRef}
onChangeText={(value) => {
email.current = value;
}}
testID="input.email"
onErrorCheck={(e) => setError(e)}
returnKeyLabel="Next"
returnKeyType="next"
autoComplete="email"
validationType="email"
autoCorrect={false}
autoCapitalize="none"
errorMessage="Email is invalid"
placeholder="Enter your email"
defaultValue={email.current}
editable={step === LoginSteps.emailAuth && !loading}
onSubmit={() => {
if (step === LoginSteps.emailAuth) {
login();
} else {
passwordInputRef.current?.focus();
}
width: 100,
height: 5,
backgroundColor: colors.primary.accent,
borderRadius: 2,
marginRight: 7
}}
/>
{step === LoginSteps.passwordAuth && (
<>
<Input
fwdRef={passwordInputRef}
onChangeText={(value) => {
password.current = value;
}}
testID="input.password"
returnKeyLabel="Done"
returnKeyType="done"
secureTextEntry
autoComplete="password"
autoCapitalize="none"
autoCorrect={false}
placeholder="Password"
marginBottom={0}
editable={!loading}
defaultValue={password.current}
onSubmit={() => login()}
/>
<Button
title="Forgot your password?"
style={{
alignSelf: "flex-end",
height: 30,
paddingHorizontal: 0
}}
onPress={() => {
if (loading) return;
SheetManager.show("forgotpassword_sheet", email.current);
}}
textStyle={{
textDecorationLine: "underline"
}}
fontSize={SIZE.xs}
type="plain"
/>
</>
)}
<View
style={{
marginTop: 25
width: 20,
height: 5,
backgroundColor: colors.secondary.background,
borderRadius: 2
}}
>
/>
</View>
<Heading
style={{
marginBottom: 25,
marginTop: 10
}}
extraBold
size={SIZE.xxl}
>
Login to your {"\n"}account
</Heading>
</View>
<View
style={{
width: DDS.isTab
? focused
? "50%"
: "49.99%"
: focused
? "100%"
: "99.9%",
backgroundColor: colors.primary.background,
alignSelf: "center",
paddingHorizontal: 20
}}
>
<Input
fwdRef={emailInputRef}
onChangeText={(value) => {
email.current = value;
}}
testID="input.email"
onErrorCheck={(e) => setError(e)}
returnKeyLabel="Next"
returnKeyType="next"
autoComplete="email"
validationType="email"
autoCorrect={false}
autoCapitalize="none"
errorMessage="Email is invalid"
placeholder="Enter your email"
defaultValue={email.current}
editable={step === LoginSteps.emailAuth && !loading}
onSubmit={() => {
passwordInputRef.current?.focus();
}}
/>
{step === LoginSteps.passwordAuth && (
<>
<Input
fwdRef={passwordInputRef}
onChangeText={(value) => {
password.current = value;
}}
testID="input.password"
returnKeyLabel="Done"
returnKeyType="done"
secureTextEntry
autoComplete="password"
autoCapitalize="none"
autoCorrect={false}
placeholder="Password"
marginBottom={0}
editable={!loading}
defaultValue={password.current}
onSubmit={() => login()}
/>
<Button
loading={loading}
title="Forgot your password?"
style={{
alignSelf: "flex-end",
height: 30,
paddingHorizontal: 0
}}
onPress={() => {
if (loading) return;
login();
SheetManager.show("forgotpassword_sheet", email.current);
}}
style={{
width: 250,
borderRadius: 100
textStyle={{
textDecorationLine: "underline"
}}
height={50}
fontSize={SIZE.md}
type="accent"
title={!loading ? "Continue" : null}
fontSize={SIZE.xs}
type="plain"
/>
</>
)}
{step === LoginSteps.passwordAuth && (
<Button
title="Cancel logging in"
style={{
alignSelf: "center",
height: 30,
marginTop: 10
}}
onPress={() => {
if (loading) return;
setStep(LoginSteps.emailAuth);
setLoading(false);
}}
textStyle={{
textDecorationLine: "underline"
}}
fontSize={SIZE.xs}
type="errorShade"
/>
)}
<View
style={{
marginTop: 25
}}
>
<Button
loading={loading}
onPress={() => {
if (loading) return;
login();
}}
style={{
width: 250,
borderRadius: 100
}}
fontSize={SIZE.md}
type="accent"
title={!loading ? "Continue" : null}
/>
{!loading ? (
<TouchableOpacity
onPress={() => {
if (loading) return;
changeMode(1);
}}
activeOpacity={0.8}
style={{
alignSelf: "center",
marginTop: 12,
paddingVertical: 12
}}
{step === LoginSteps.passwordAuth && (
<Button
title="Cancel logging in"
style={{
alignSelf: "center",
height: 30,
marginTop: 10
}}
onPress={() => {
if (loading) return;
setStep(LoginSteps.emailAuth);
setLoading(false);
}}
textStyle={{
textDecorationLine: "underline"
}}
fontSize={SIZE.xs}
type="errorShade"
/>
)}
{!loading ? (
<TouchableOpacity
onPress={() => {
if (loading) return;
changeMode(1);
}}
activeOpacity={0.8}
style={{
alignSelf: "center",
marginTop: 12,
paddingVertical: 12
}}
>
<Paragraph
size={SIZE.xs + 1}
color={colors.secondary.paragraph}
>
Don't have an account?{" "}
<Paragraph
size={SIZE.xs + 1}
color={colors.secondary.paragraph}
style={{ color: colors.primary.accent }}
>
Don't have an account?{" "}
<Paragraph
size={SIZE.xs + 1}
style={{ color: colors.primary.accent }}
>
Sign up
</Paragraph>
Sign up
</Paragraph>
</TouchableOpacity>
) : null}
</View>
</Paragraph>
</TouchableOpacity>
) : null}
</View>
</View>
</KeyboardAwareScrollView>
</View>
</>
);
};

View File

@@ -87,9 +87,6 @@ export const SessionExpired = () => {
MMKV.clearStore();
clearAllStores();
setVisible(false);
useUserStore.setState({
disableAppLockRequests: false
});
} catch (e) {
ToastManager.show({
heading: e.message,
@@ -112,11 +109,7 @@ export const SessionExpired = () => {
if (!res) throw new Error("no token found");
if (db.tokenManager._isTokenExpired(res))
throw new Error("token expired");
const key = await db.user.getEncryptionKey();
if (!key) throw new Error("No encryption key found.");
Sync.run("global", false, "full", async (complete) => {
Sync.run("global", false, true, async (complete) => {
if (!complete) {
let user = await db.user.getUser();
if (!user) return;
@@ -131,6 +124,7 @@ export const SessionExpired = () => {
setVisible(false);
});
} catch (e) {
console.log(e);
let user = await db.user.getUser();
if (!user) return;
email.current = user.email;
@@ -157,9 +151,6 @@ export const SessionExpired = () => {
await sleep(300);
passwordInputRef.current?.focus();
setFocused(true);
useUserStore.setState({
disableAppLockRequests: true
});
}}
enableSheetKeyboardHandler={true}
visible={true}

View File

@@ -17,38 +17,26 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { useThemeColors } from "@notesnook/theme";
import React, { useRef, useState } from "react";
import { TouchableOpacity, View, useWindowDimensions } from "react-native";
import { KeyboardAwareScrollView } from "react-native-keyboard-aware-scroll-view";
import { db } from "../../common/database";
import { DDS } from "../../services/device-detection";
import { ToastManager } from "../../services/event-manager";
import { clearMessage, setEmailVerifyMessage } from "../../services/message";
import { useSettingStore } from "../../stores/use-setting-store";
import PremiumService from "../../services/premium";
import { useThemeColors } from "@notesnook/theme";
import { useUserStore } from "../../stores/use-user-store";
import { openLinkInBrowser } from "../../utils/functions";
import { SIZE } from "../../utils/size";
import { Loading } from "../loading";
import { PaywallComponent } from "../premium/component";
import { sleep } from "../../utils/time";
import { Button } from "../ui/button";
import Input from "../ui/input";
import Heading from "../ui/typography/heading";
import Paragraph from "../ui/typography/paragraph";
import { hideAuth } from "./common";
import { useSettingStore } from "../../stores/use-setting-store";
import SettingsService from "../../services/settings";
import { AuthHeader } from "./header";
import { SignupContext } from "./signup-context";
const SignupSteps = {
signup: 0,
selectPlan: 1,
createAccount: 2
};
export const Signup = ({ changeMode, welcome }) => {
const [currentStep, setCurrentStep] = useState(SignupSteps.signup);
export const Signup = ({ changeMode, trial }) => {
const { colors } = useThemeColors();
const email = useRef();
const emailInputRef = useRef();
@@ -61,7 +49,7 @@ export const Signup = ({ changeMode, welcome }) => {
const setUser = useUserStore((state) => state.setUser);
const setLastSynced = useUserStore((state) => state.setLastSynced);
const deviceMode = useSettingStore((state) => state.deviceMode);
const { height } = useWindowDimensions();
const { width, height } = useWindowDimensions();
const validateInfo = () => {
if (!password.current || !email.current || !confirmPassword.current) {
ToastManager.show({
@@ -80,20 +68,22 @@ export const Signup = ({ changeMode, welcome }) => {
const signup = async () => {
if (!validateInfo() || error) return;
if (loading) return;
setLoading(true);
try {
setCurrentStep(SignupSteps.createAccount);
await db.user.signup(email.current.toLowerCase(), password.current);
let user = await db.user.getUser();
setUser(user);
setLastSynced(await db.lastSynced());
clearMessage();
setEmailVerifyMessage();
setCurrentStep(SignupSteps.selectPlan);
return true;
hideAuth();
await sleep(300);
if (trial) {
PremiumService.sheet(null, null, true);
} else {
PremiumService.showVerifyEmailDialog();
}
} catch (e) {
setCurrentStep(SignupSteps.signup);
setLoading(false);
ToastManager.show({
heading: "Signup failed",
@@ -101,266 +91,215 @@ export const Signup = ({ changeMode, welcome }) => {
type: "error",
context: "local"
});
return false;
}
};
return (
<SignupContext.Provider
value={{
signup: signup
}}
>
{currentStep === SignupSteps.signup ? (
<>
<AuthHeader welcome={welcome} />
<KeyboardAwareScrollView
<>
<View
style={{
borderRadius: DDS.isTab ? 5 : 0,
backgroundColor: colors.primary.background,
zIndex: 10,
width: "100%",
alignSelf: "center"
}}
>
<View
style={{
justifyContent: "flex-end",
paddingHorizontal: 20,
backgroundColor: colors.secondary.background,
marginBottom: 20,
borderBottomWidth: 0.8,
borderBottomColor: colors.primary.border,
alignSelf: deviceMode !== "mobile" ? "center" : undefined,
borderWidth: deviceMode !== "mobile" ? 1 : null,
borderColor: deviceMode !== "mobile" ? colors.primary.border : null,
borderRadius: deviceMode !== "mobile" ? 20 : null,
marginTop: deviceMode !== "mobile" ? 50 : null,
width: deviceMode === "mobile" ? null : "50%",
minHeight: height * 0.4
}}
>
<View
style={{
width: "100%"
flexDirection: "row"
}}
contentContainerStyle={{
minHeight: "90%"
}}
nestedScrollEnabled
enableAutomaticScroll={false}
keyboardShouldPersistTaps="handled"
>
<View
style={{
borderRadius: DDS.isTab ? 5 : 0,
backgroundColor: colors.primary.background,
zIndex: 10,
width: "100%",
alignSelf: "center",
height: "100%"
width: 100,
height: 5,
backgroundColor: colors.primary.accent,
borderRadius: 2,
marginRight: 7
}}
/>
<View
style={{
width: 20,
height: 5,
backgroundColor: colors.secondary.background,
borderRadius: 2
}}
/>
</View>
<Heading
extraBold
style={{
marginBottom: 25,
marginTop: 10
}}
size={SIZE.xxl}
>
Create your {"\n"}account
</Heading>
</View>
<View
style={{
width: DDS.isTab ? "50%" : "100%",
paddingHorizontal: 20,
backgroundColor: colors.primary.background,
alignSelf: "center"
}}
>
<Input
fwdRef={emailInputRef}
onChangeText={(value) => {
email.current = value;
}}
testID="input.email"
onErrorCheck={(e) => setError(e)}
returnKeyLabel="Next"
returnKeyType="next"
autoComplete="email"
validationType="email"
autoCorrect={false}
autoCapitalize="none"
errorMessage="Email is invalid"
placeholder="Email"
onSubmit={() => {
passwordInputRef.current?.focus();
}}
/>
<Input
fwdRef={passwordInputRef}
onChangeText={(value) => {
password.current = value;
}}
testID="input.password"
onErrorCheck={(e) => setError(e)}
returnKeyLabel="Next"
returnKeyType="next"
secureTextEntry
autoComplete="password"
autoCapitalize="none"
validationType="password"
autoCorrect={false}
placeholder="Password"
onSubmit={() => {
confirmPasswordInputRef.current?.focus();
}}
/>
<Input
fwdRef={confirmPasswordInputRef}
onChangeText={(value) => {
confirmPassword.current = value;
}}
testID="input.confirmPassword"
onErrorCheck={(e) => setError(e)}
returnKeyLabel="Signup"
returnKeyType="done"
secureTextEntry
autoComplete="password"
autoCapitalize="none"
autoCorrect={false}
validationType="confirmPassword"
customValidator={() => password.current}
placeholder="Confirm password"
marginBottom={12}
onSubmit={signup}
/>
<Paragraph
style={{
marginBottom: 25
}}
size={SIZE.xs}
color={colors.secondary.paragraph}
>
By signing up, you agree to our{" "}
<Paragraph
size={SIZE.xs}
onPress={() => {
openLinkInBrowser("https://notesnook.com/tos", colors);
}}
style={{
textDecorationLine: "underline"
}}
color={colors.primary.accent}
>
<View
style={{
justifyContent: "flex-end",
paddingHorizontal: 16,
backgroundColor: colors.secondary.background,
marginBottom: 20,
borderBottomWidth: 0.8,
borderBottomColor: colors.primary.border,
alignSelf: deviceMode !== "mobile" ? "center" : undefined,
borderWidth: deviceMode !== "mobile" ? 1 : null,
borderColor:
deviceMode !== "mobile" ? colors.primary.border : null,
borderRadius: deviceMode !== "mobile" ? 20 : null,
marginTop: deviceMode !== "mobile" ? 50 : null,
width: deviceMode === "mobile" ? null : "50%",
minHeight: height * 0.25
}}
>
<View
style={{
flexDirection: "row"
}}
>
<View
style={{
width: 100,
height: 5,
backgroundColor: colors.primary.accent,
borderRadius: 2,
marginRight: 7
}}
/>
Terms of Service{" "}
</Paragraph>
and{" "}
<Paragraph
size={SIZE.xs}
onPress={() => {
openLinkInBrowser("https://notesnook.com/privacy", colors);
}}
style={{
textDecorationLine: "underline"
}}
color={colors.primary.accent}
>
Privacy Policy.
</Paragraph>{" "}
You also agree to recieve marketing emails from us which you can
opt-out of from app settings.
</Paragraph>
<View
style={{
width: 20,
height: 5,
backgroundColor: colors.secondary.background,
borderRadius: 2
}}
/>
</View>
<Heading
extraBold
style={{
marginBottom: 25,
marginTop: 10
}}
size={SIZE.xxl}
>
Create your account
</Heading>
</View>
<View
style={{
width: DDS.isTab ? "50%" : "100%",
paddingHorizontal: 16,
backgroundColor: colors.primary.background,
flexGrow: 1
}}
>
<Input
fwdRef={emailInputRef}
onChangeText={(value) => {
email.current = value;
}}
testID="input.email"
onErrorCheck={(e) => setError(e)}
returnKeyLabel="Next"
returnKeyType="next"
autoComplete="email"
validationType="email"
autoCorrect={false}
autoCapitalize="none"
errorMessage="Email is invalid"
placeholder="Email"
blurOnSubmit={false}
onSubmit={() => {
if (!email.current) return;
passwordInputRef.current?.focus();
}}
/>
<Input
fwdRef={passwordInputRef}
onChangeText={(value) => {
password.current = value;
}}
testID="input.password"
onErrorCheck={(e) => setError(e)}
returnKeyLabel="Next"
returnKeyType="next"
secureTextEntry
autoComplete="password"
autoCapitalize="none"
blurOnSubmit={false}
validationType="password"
autoCorrect={false}
placeholder="Password"
onSubmit={() => {
if (!password.current) return;
confirmPasswordInputRef.current?.focus();
}}
/>
<Input
fwdRef={confirmPasswordInputRef}
onChangeText={(value) => {
confirmPassword.current = value;
}}
testID="input.confirmPassword"
onErrorCheck={(e) => setError(e)}
returnKeyLabel="Signup"
returnKeyType="done"
secureTextEntry
autoComplete="password"
autoCapitalize="none"
autoCorrect={false}
blurOnSubmit={false}
validationType="confirmPassword"
customValidator={() => password.current}
placeholder="Confirm password"
marginBottom={12}
onSubmit={signup}
/>
<Button
title={!loading ? "Continue" : null}
type="accent"
loading={loading}
onPress={() => {
signup();
}}
fontSize={SIZE.md}
width="100%"
/>
<TouchableOpacity
onPress={() => {
if (loading) return;
changeMode(0);
}}
activeOpacity={0.8}
style={{
alignSelf: "center",
marginTop: 12,
paddingVertical: 12
}}
>
<Paragraph
size={SIZE.xs + 1}
color={colors.secondary.paragraph}
>
Already have an account?{" "}
<Paragraph
size={SIZE.xs + 1}
style={{ color: colors.primary.accent }}
>
Login
</Paragraph>
</Paragraph>
</TouchableOpacity>
</View>
<Button
title={!loading ? "Continue" : null}
type="accent"
loading={loading}
onPress={signup}
fontSize={SIZE.md}
style={{
marginRight: 12,
width: 250,
borderRadius: 100
}}
/>
<TouchableOpacity
onPress={() => {
if (loading) return;
changeMode(0);
}}
activeOpacity={0.8}
style={{
alignSelf: "center",
marginTop: 12,
paddingVertical: 12
}}
>
<Paragraph size={SIZE.xs + 1} color={colors.secondary.paragraph}>
Already have an account?{" "}
<Paragraph
style={{
marginBottom: 25,
paddingHorizontal: 16,
textAlign: "center"
}}
size={SIZE.xs}
color={colors.secondary.paragraph}
size={SIZE.xs + 1}
style={{ color: colors.primary.accent }}
>
By signing up, you agree to our{" "}
<Paragraph
size={SIZE.xs}
onPress={() => {
openLinkInBrowser("https://notesnook.com/tos", colors);
}}
style={{
textDecorationLine: "underline"
}}
color={colors.primary.accent}
>
Terms of Service{" "}
</Paragraph>
and{" "}
<Paragraph
size={SIZE.xs}
onPress={() => {
openLinkInBrowser("https://notesnook.com/privacy", colors);
}}
style={{
textDecorationLine: "underline"
}}
color={colors.primary.accent}
>
Privacy Policy.
</Paragraph>{" "}
You also agree to recieve marketing emails from us which you can
opt-out of from app settings.
Login
</Paragraph>
</View>
</KeyboardAwareScrollView>
</>
) : currentStep === SignupSteps.createAccount ? (
<>
<Loading
title="Setting up your account..."
description="Your account is almost ready, please wait..."
/>
</>
) : (
<>
<PaywallComponent
close={() => {
hideAuth();
}}
setupAccount={() => {
setCurrentStep(SignupSteps.createAccount);
}}
isModal={false}
/>
</>
)}
</SignupContext.Provider>
</Paragraph>
</TouchableOpacity>
</View>
</View>
</>
);
};

View File

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

View File

@@ -18,9 +18,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { useRef, useState } from "react";
import { TextInput } from "react-native";
import { db } from "../../common/database";
import { ToastManager, eSendEvent } 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";
@@ -34,18 +33,15 @@ export const LoginSteps = {
passwordAuth: 3
};
export const useLogin = (
onFinishLogin?: () => void,
sessionExpired = false
) => {
export const useLogin = (onFinishLogin, sessionExpired = false) => {
const [error, setError] = useState(false);
const [loading, setLoading] = useState(false);
const setUser = useUserStore((state) => state.setUser);
const [step, setStep] = useState(LoginSteps.emailAuth);
const email = useRef<string>();
const password = useRef<string>();
const emailInputRef = useRef<TextInput>(null);
const passwordInputRef = useRef<TextInput>(null);
const email = useRef();
const password = useRef();
const emailInputRef = useRef();
const passwordInputRef = useRef();
const validateInfo = () => {
if (
@@ -72,57 +68,44 @@ export const useLogin = (
setLoading(true);
switch (step) {
case LoginSteps.emailAuth: {
if (!email.current) {
setLoading(false);
return;
}
const mfaInfo = await db.user.authenticateEmail(email.current);
console.log("email auth", mfaInfo);
if (mfaInfo) {
TwoFactorVerification.present(
async (
mfa: { code: string; method: string },
callback: (value: boolean) => void
) => {
try {
const success = await db.user.authenticateMultiFactorCode(
mfa.code,
mfa.method
);
TwoFactorVerification.present(async (mfa, callback) => {
try {
const success = await db.user.authenticateMultiFactorCode(
mfa.code,
mfa.method
);
if (success) {
setStep(LoginSteps.passwordAuth);
setLoading(false);
setTimeout(() => {
passwordInputRef.current?.focus();
}, 1);
callback && callback(true);
}
callback && callback(false);
} catch (e) {
callback && callback(false);
if ((e as Error).message === "invalid_grant") {
eSendEvent(eCloseSheet, "two_factor_verify");
setLoading(false);
setStep(LoginSteps.emailAuth);
}
if (success) {
setStep(LoginSteps.passwordAuth);
setLoading(false);
setTimeout(() => {
passwordInputRef.current?.focus();
}, 1);
callback && callback(true);
}
},
mfaInfo
);
callback && callback(false);
} catch (e) {
callback && callback(false);
if (e.message === "invalid_grant") {
eSendEvent(eCloseSheet, "two_factor_verify");
setLoading(false);
setStep(LoginSteps.emailAuth);
}
}
}, mfaInfo);
} else {
finishWithError(new Error("Unable to send 2FA code"));
}
break;
}
case LoginSteps.passwordAuth: {
if (!email.current || !password.current) {
setLoading(false);
return;
}
await db.user.authenticatePassword(
email.current,
password.current,
undefined,
null,
sessionExpired
);
finishLogin();
@@ -131,11 +114,11 @@ export const useLogin = (
}
setLoading(false);
} catch (e) {
finishWithError(e as Error);
finishWithError(e);
}
};
const finishWithError = async (e: Error) => {
const finishWithError = async (e) => {
if (e.message === "invalid_grant") setStep(LoginSteps.emailAuth);
setLoading(false);
ToastManager.show({

View File

@@ -18,7 +18,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { useThemeColors } from "@notesnook/theme";
import { useRoute } from "@react-navigation/native";
import React, { useCallback, useEffect } from "react";
import { Keyboard, View } from "react-native";
import Animated, {
@@ -55,7 +54,6 @@ const FloatingButton = ({
const deviceMode = useSettingStore((state) => state.deviceMode);
const selectionMode = useSelectionStore((state) => state.selectionMode);
const translate = useSharedValue(0);
const route = useRoute();
const animatedStyle = useAnimatedStyle(() => {
return {
@@ -142,13 +140,7 @@ const FloatingButton = ({
}}
>
<Icon
name={
route.name === "Notebooks"
? "notebook-plus"
: route.name === "Trash"
? "delete"
: "plus"
}
name={title === "Clear all trash" ? "delete" : "plus"}
color={colors.primary.accentForeground}
size={SIZE.xxl}
/>

View File

@@ -21,16 +21,14 @@ 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";
import { useThemeColors } from "@notesnook/theme";
export const Container = ({ children }: PropsWithChildren) => {
const { colors } = useThemeColors();
const insets = useGlobalSafeAreaInsets();
const keyboard = useKeyboard();
const [height, setHeight] = useState(0);
const windowHeightRef = useRef(Dimensions.get("window").height);
const { height: windowHeight } = useWindowDimensions();
const timerRef = useRef<NodeJS.Timeout>();
useEffect(() => {
if (windowHeight !== windowHeightRef.current) {
setHeight(0);
@@ -45,17 +43,12 @@ export const Container = ({ children }: PropsWithChildren) => {
paddingTop: Platform.OS === "android" ? 0 : insets.top,
paddingBottom: Platform.OS === "android" ? 0 : insets.bottom,
height: height || "100%",
width: "100%",
backgroundColor: colors.primary.background
width: "100%"
}}
onLayout={(event) => {
const height = event.nativeEvent.layout.height;
clearTimeout(timerRef.current);
timerRef.current = setTimeout(() => {
if (!keyboard.keyboardShown) {
setHeight(height);
}
}, 500);
if (!keyboard.keyboardShown) {
setHeight(event.nativeEvent.layout.height);
}
}}
>
{children}

View File

@@ -23,7 +23,6 @@ import { AnnouncementDialog } from "../announcements";
import AuthModal from "../auth/auth-modal";
import { SessionExpired } from "../auth/session-expired";
import { Dialog } from "../dialog";
import { AppLockPassword } from "../dialogs/applock-password";
import JumpToSectionDialog from "../dialogs/jump-to-section";
import { LoadingDialog } from "../dialogs/loading";
import PDFPreview from "../dialogs/pdf-preview";
@@ -36,7 +35,8 @@ import { Expiring } from "../premium/expiring";
import SheetProvider from "../sheet-provider";
import RateAppSheet from "../sheets/rate-app";
import RecoveryKeySheet from "../sheets/recovery-key";
import Progress from "../dialogs/progress";
import RestoreDataSheet from "../sheets/restore-data";
import { AppLockPassword } from "../dialogs/applock-password";
const DialogProvider = () => {
const { colors } = useThemeColors();
@@ -52,6 +52,7 @@ const DialogProvider = () => {
<RecoveryKeySheet colors={colors} />
<SheetProvider />
<SheetProvider context="sync_progress" />
<RestoreDataSheet />
<ResultDialog />
<VaultDialog colors={colors} />
<RateAppSheet />
@@ -61,7 +62,6 @@ const DialogProvider = () => {
<SessionExpired />
<PDFPreview />
<JumpToSectionDialog />
<Progress />
</>
);
};

View File

@@ -45,8 +45,8 @@ const DialogButtons = ({
{
backgroundColor: colors.secondary.background,
height: 60,
borderBottomRightRadius: 5,
borderBottomLeftRadius: 5,
borderBottomRightRadius: 10,
borderBottomLeftRadius: 10,
paddingHorizontal: 12,
borderTopWidth: 0.7,
borderTopColor: getColorLinearShade(

View File

@@ -32,29 +32,21 @@ const DialogContainer = ({
}: ViewProps & {
width?: any;
height?: any;
noBorder?: boolean;
}) => {
const { colors } = useThemeColors();
return (
<View
{...restProps}
style={[
style,
{
width: width || DDS.isTab ? 500 : "85%",
maxHeight: height || 450,
borderRadius: 10,
backgroundColor: colors.primary.background,
paddingTop: 12
},
restProps?.noBorder
? {}
: {
...getElevationStyle(5),
...getContainerBorder(colors.secondary.background, 0.8, 0.05)
}
]}
style={{
...getElevationStyle(5),
width: width || DDS.isTab ? 500 : "85%",
maxHeight: height || 450,
borderRadius: 10,
backgroundColor: colors.primary.background,
paddingTop: 12,
...getContainerBorder(colors.secondary.background, 0.8, 0.05)
}}
/>
);
};

View File

@@ -25,7 +25,7 @@ type DialogInfo = {
paragraph?: string;
positiveText?: string;
negativeText?: string;
positivePress?: (...args: any[]) => void;
positivePress?: (value: any) => void;
onClose?: () => void;
positiveType?:
| "transparent"
@@ -44,16 +44,6 @@ type DialogInfo = {
// eslint-disable-next-line @typescript-eslint/ban-types
context: "global" | "local" | (string & {});
secureTextEntry?: boolean;
keyboardType?: string;
check?: {
info: string;
type?: string;
defaultValue?: boolean;
};
notice: {
text: string;
type: "alert" | "information";
};
};
export function presentDialog(data: Partial<DialogInfo>): void {

View File

@@ -36,8 +36,6 @@ import DialogButtons from "./dialog-buttons";
import DialogHeader from "./dialog-header";
import { useCallback } from "react";
import { Button } from "../ui/button";
import { getContainerBorder } from "../../utils/colors";
import { Notice } from "../ui/notice";
export const Dialog = ({ context = "global" }) => {
const { colors } = useThemeColors();
@@ -63,8 +61,7 @@ export const Dialog = ({ context = "global" }) => {
disableBackdropClosing: false,
check: {
info: "Check",
type: "transparent",
defaultValue: false
type: "transparent"
}
});
@@ -88,7 +85,7 @@ export const Dialog = ({ context = "global" }) => {
if (!data.context) data.context = "global";
if (data.context !== context) return;
setDialogInfo(data);
setChecked(data.check?.defaultValue);
setChecked(false);
values.current.inputValue = data.defaultValue;
setVisible(true);
},
@@ -124,8 +121,7 @@ export const Dialog = ({ context = "global" }) => {
maxHeight: 450,
borderRadius: 5,
backgroundColor: colors.primary.background,
paddingTop: 12,
...getContainerBorder(colors.primary.border, 0.5)
paddingTop: 12
};
return visible ? (
@@ -176,25 +172,11 @@ export const Dialog = ({ context = "global" }) => {
onSubmit={onPressPositive}
returnKeyLabel="Done"
returnKeyType="done"
keyboardType={dialogInfo.keyboardType || "default"}
placeholder={dialogInfo.inputPlaceholder}
/>
</View>
) : null}
{dialogInfo?.notice ? (
<View
style={{
paddingHorizontal: 12
}}
>
<Notice
type={dialogInfo.notice.type || "information"}
text={dialogInfo.notice.text}
/>
</View>
) : null}
{dialogInfo.check ? (
<>
<Button
@@ -213,7 +195,7 @@ export const Dialog = ({ context = "global" }) => {
iconSize={20}
width="100%"
title={dialogInfo.check.info}
type={checked ? dialogInfo.check.type || "plain" : "plain"}
type={checked ? dialogInfo.check.type : "gray"}
/>
</>
) : null}

View File

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

View File

@@ -17,14 +17,8 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import React, { useState } from "react";
import {
Image,
Platform,
ScrollView,
TouchableOpacity,
View
} from "react-native";
import { Image as ImageType } from "react-native-image-crop-picker";
import { Image, ScrollView, TouchableOpacity, View } from "react-native";
import { ImagePickerResponse } from "react-native-image-picker";
import { useThemeColors } from "../../../../../../packages/theme/dist";
import { presentSheet } from "../../../services/event-manager";
import { SIZE } from "../../../utils/size";
@@ -38,7 +32,7 @@ export default function AttachImage({
onAttach,
close
}: {
response: ImageType[];
response: ImagePickerResponse;
onAttach: ({ compress }: { compress: boolean }) => void;
close: ((ctx?: string | undefined) => void) | undefined;
}) {
@@ -64,17 +58,14 @@ export default function AttachImage({
}}
>
<Paragraph style={{ color: colors.primary.paragraph, marginBottom: 6 }}>
Attaching {response?.length} image(s):
Attaching {response.assets?.length} image(s):
</Paragraph>
<ScrollView horizontal>
{response?.map?.((item) => (
<TouchableOpacity key={item.filename} activeOpacity={0.9}>
{response.assets?.map((item) => (
<TouchableOpacity key={item.fileName} activeOpacity={0.9}>
<Image
source={{
uri:
Platform.OS === "ios"
? item.sourceURL || item.path
: item.path
uri: item.uri
}}
style={{
width: 100,
@@ -151,7 +142,7 @@ export default function AttachImage({
<Button
title={`${
(response?.length || 0) > 1 ? "Attach Images" : "Attach Image"
(response.assets?.length || 0) > 1 ? "Attach Images" : "Attach Image"
}`}
type="accent"
width="100%"
@@ -165,16 +156,10 @@ export default function AttachImage({
);
}
AttachImage.present = (response: ImageType[], context?: string) => {
return new Promise<
| {
compress: boolean;
}
| undefined
>((resolve) => {
AttachImage.present = (response: ImagePickerResponse) => {
return new Promise((resolve) => {
let resolved = false;
presentSheet({
context: context,
component: (ref, close, update) => (
<AttachImage
response={response}

View File

@@ -75,7 +75,7 @@ const ColorPicker = ({
style={{
width: "100%",
height: 250,
backgroundColor: colors.primary.background,
backgroundColor: selectedColor,
borderRadius: 10
}}
>
@@ -146,16 +146,12 @@ const ColorPicker = ({
onChangeText={(value) => {
title.current = value;
}}
testID="color-title-input"
defaultValue={title.current}
placeholder={title.current || "Color title"}
/>
<Button
title="Add color"
style={{
marginBottom: 10
}}
onPress={async () => {
if (!selectedColor)
return ToastManager.error(
@@ -174,6 +170,7 @@ const ColorPicker = ({
return ToastManager.error(
new Error(`Color #${selectedColor} already exists`)
);
const id = await db.colors.add({
title: title.current,
colorCode: selectedColor

View File

@@ -44,7 +44,6 @@ import { ProgressBarComponent } from "../../ui/svg/lazy";
import Paragraph from "../../ui/typography/paragraph";
import { sleep } from "../../../utils/time";
import { MMKV } from "../../../common/database/mmkv";
import { deleteCacheFileByPath } from "../../../common/filesystem/io";
const WIN_WIDTH = Dimensions.get("window").width;
const WIN_HEIGHT = Dimensions.get("window").height;
@@ -125,7 +124,6 @@ const PDFPreview = () => {
);
const close = () => {
deleteCacheFileByPath(pdfSource);
setPDFSource(null);
setVisible(false);
setPassword("");

View File

@@ -1,198 +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 { useThemeColors } from "@notesnook/theme";
import React, { useEffect } from "react";
import { View } from "react-native";
import { eSendEvent, eSubscribeEvent } from "../../../services/event-manager";
import { SIZE } from "../../../utils/size";
import { Dialog } from "../../dialog";
import BaseDialog from "../../dialog/base-dialog";
import DialogContainer from "../../dialog/dialog-container";
import { Button } from "../../ui/button";
import { ProgressBarComponent } from "../../ui/svg/lazy";
import Heading from "../../ui/typography/heading";
import Paragraph from "../../ui/typography/paragraph";
export type ProgressOptions = {
progress?: string;
cancelCallback?: () => void;
title?: string;
paragraph?: string;
fillBackground?: boolean;
canHideProgress?: boolean;
};
export const PROGRESS_EVENTS = {
start: "startProgress",
end: "endProgress",
update: "updateProgress"
};
export default function Progress() {
const { colors } = useThemeColors();
const [progress, setProgress] = React.useState<string | undefined>();
const [visible, setVisible] = React.useState(false);
const cancelCallback = React.useRef<() => void>();
const [data, setData] = React.useState<{
title?: string;
paragraph?: string;
fillBackground?: boolean;
canHideProgress?: boolean;
}>();
useEffect(() => {
const events = [
eSubscribeEvent(PROGRESS_EVENTS.start, (options: ProgressOptions) => {
setProgress(options.progress);
cancelCallback.current = options.cancelCallback;
console.log("options", options.fillBackground);
setData({
title: options.title,
paragraph: options.paragraph,
fillBackground: options.fillBackground,
canHideProgress: options.canHideProgress
});
setVisible(true);
}),
eSubscribeEvent(PROGRESS_EVENTS.end, () => {
setProgress(undefined);
setVisible(false);
setData(undefined);
cancelCallback.current?.();
cancelCallback.current = undefined;
}),
eSubscribeEvent(PROGRESS_EVENTS.update, (options: ProgressOptions) => {
setProgress(options.progress);
if (options.cancelCallback) {
cancelCallback.current = options.cancelCallback;
}
const data: ProgressOptions = {};
if (options.title) data.title = options.title;
if (options.paragraph) data.paragraph = options.paragraph;
if (options.fillBackground)
data.fillBackground = options.fillBackground;
setData((current) => {
return {
...current,
...data
};
});
})
];
return () => {
events.forEach((event) => event?.unsubscribe());
cancelCallback.current = undefined;
};
}, []);
return !visible ? null : (
<BaseDialog
background={data?.fillBackground ? colors.primary.background : undefined}
visible
>
<DialogContainer
style={{
paddingHorizontal: 12,
paddingBottom: 10
}}
noBorder={data?.fillBackground ? true : false}
>
<Dialog context="local" />
<View
style={{
justifyContent: "center",
alignItems: "center",
paddingHorizontal: 12,
gap: 10,
paddingBottom: 20
}}
>
<Heading
style={{
textAlign: "center"
}}
color={colors.primary.paragraph}
size={SIZE.lg}
>
{data?.title}
</Heading>
<Paragraph
style={{
textAlign: "center"
}}
color={colors.secondary.paragraph}
>
{progress ? progress : data?.paragraph}
</Paragraph>
<View
style={{
flexDirection: "row",
width: 100,
paddingVertical: 12
}}
>
<ProgressBarComponent
height={5}
width={100}
animated={true}
useNativeDriver
indeterminate
indeterminateAnimationDuration={2000}
unfilledColor={colors.secondary.background}
color={colors.primary.accent}
borderWidth={0}
/>
</View>
{data?.canHideProgress ? null : (
<Button
title={cancelCallback.current ? "Cancel" : "Hide"}
type="secondaryAccented"
onPress={() => {
if (cancelCallback.current) {
cancelCallback.current?.();
}
setVisible(false);
setProgress(undefined);
setData(undefined);
}}
width="100%"
/>
)}
</View>
</DialogContainer>
</BaseDialog>
);
}
export function startProgress(options: ProgressOptions) {
eSendEvent(PROGRESS_EVENTS.start, options);
}
export function endProgress() {
eSendEvent(PROGRESS_EVENTS.end);
}
export function updateProgress(options: ProgressOptions) {
eSendEvent(PROGRESS_EVENTS.update, options);
}

View File

@@ -128,7 +128,7 @@ export class VaultDialog extends Component {
: this.state.copyNote
? "Unlock note to copy it. If biometrics are not working, you can enter device pin to unlock vault."
: this.state.goToEditor
? "Unlock note to open it in editor."
? "Unlock note to open it in editor. If biometrics are not working, you can enter device pin to unlock vault."
: "Enter vault password to unlock note. If biometrics are not working, you can enter device pin to unlock vault."
: "Enter vault password to lock note. If biometrics are not working, you can enter device pin to lock note.");
}
@@ -586,7 +586,7 @@ export class VaultDialog extends Component {
}
async _copyNote(note) {
Clipboard.setString((await convertNoteToText(note, true)) || "");
Clipboard.setString(await convertNoteToText(note));
ToastManager.show({
heading: "Note copied",
type: "success",
@@ -602,7 +602,7 @@ export class VaultDialog extends Component {
await Share.open({
heading: "Share note",
failOnCancel: false,
message: (await convertNoteToText(note)) || ""
message: await convertNoteToText(note)
});
} catch (e) {
console.error(e);

View File

@@ -23,7 +23,10 @@ import { SafeAreaProvider, SafeAreaView } from "react-native-safe-area-context";
import { Dialog } from "../dialog";
import { Issue } from "../sheets/github/issue";
const error = (stack: string, component: string) => `
const error = (
stack: string,
component: string
) => `Please let us know what happened. What steps we can take to reproduce the issue here.
_______________________________
Stacktrace: In ${component}::${stack}`;
@@ -36,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 };
}

View File

@@ -33,7 +33,6 @@ import { eScrollEvent } from "../../utils/events";
import { LeftMenus } from "./left-menus";
import { RightMenus } from "./right-menus";
import { Title } from "./title";
import { useNavigation } from "@react-navigation/native";
type HeaderRightButton = {
title: string;

View File

@@ -71,7 +71,7 @@ export const RightMenus = ({
testID={notesnook.ids.default.addBtn}
icon={renderedInRoute === "Trash" ? "delete" : "plus"}
iconSize={SIZE.xl}
type="accent"
type="shade"
hitSlop={{
top: 10,
right: 10,
@@ -82,7 +82,7 @@ export const RightMenus = ({
marginLeft: 10,
width: 32,
height: 32,
borderRadius: 100,
borderRadius: 5,
paddingHorizontal: 0,
borderWidth: 1,
borderColor: colors.primary.accent
@@ -96,8 +96,7 @@ export const RightMenus = ({
animationDuration={200}
style={{
borderRadius: 5,
backgroundColor: contextMenuColors.primary.background,
marginTop: -40
backgroundColor: contextMenuColors.primary.background
}}
onRequestClose={() => {
menuRef.current?.hide();

View File

@@ -18,13 +18,11 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import Sodium from "@ammarahmed/react-native-sodium";
import { DataURL } from "@notesnook/core";
import type { ImageAttributes } from "@notesnook/editor";
import dataurl from "@notesnook/core/dist/utils/dataurl";
import { useThemeColors } from "@notesnook/theme";
import React, { useEffect, useRef, useState } from "react";
import React, { useEffect, useState } from "react";
import { Platform, View } from "react-native";
import ImageViewer from "react-native-image-zoom-viewer";
import { db } from "../../common/database";
import downloadAttachment from "../../common/filesystem/download-attachment";
import { cacheDir } from "../../common/filesystem/utils";
import {
@@ -34,13 +32,13 @@ import {
import BaseDialog from "../dialog/base-dialog";
import { IconButton } from "../ui/icon-button";
import { ProgressBarComponent } from "../ui/svg/lazy";
import type { ImageAttributes } from "@notesnook/editor/dist/extensions/image/index";
const ImagePreview = () => {
const { colors } = useThemeColors("dialog");
const [visible, setVisible] = useState(false);
const [image, setImage] = useState<string>();
const [loading, setLoading] = useState(false);
const imageRef = useRef<ImageAttributes>();
useEffect(() => {
eSubscribeEvent("ImagePreview", open);
@@ -50,22 +48,18 @@ const ImagePreview = () => {
}, []);
const open = async (image: ImageAttributes) => {
imageRef.current = image;
setVisible(true);
setLoading(true);
setTimeout(async () => {
let hash = image.hash;
if (!hash && image.src && DataURL.toObject(image.src)) {
const data = DataURL.toObject(image.src);
if (!hash && image.src && dataurl.toObject(image.src)) {
const data = dataurl.toObject(image.src);
if (!data) return;
hash = await Sodium.hashFile({
data: data.data,
type: "base64",
uri: ""
});
if (imageRef.current) {
imageRef.current.hash = hash;
}
}
if (!hash) return;
//@ts-ignore // FIX ME
@@ -86,13 +80,7 @@ const ImagePreview = () => {
return (
visible && (
<BaseDialog
background="black"
animation="slide"
visible={true}
onRequestClose={close}
transparent
>
<BaseDialog animation="slide" visible={true} onRequestClose={close}>
<View
style={{
width: "100%",
@@ -113,21 +101,6 @@ const ImagePreview = () => {
color={colors.primary.accent}
borderColor="transparent"
/>
<IconButton
onPress={() => {
if (imageRef.current?.hash) {
db.fs().cancel(imageRef.current?.hash);
}
close();
}}
style={{
position: "absolute",
top: Platform.OS === "android" ? 35 : 0,
right: 12
}}
color={colors.static.white}
name="close"
/>
</View>
) : (
<ImageViewer
@@ -144,12 +117,13 @@ const ImagePreview = () => {
width: "100%",
justifyContent: "flex-end",
alignItems: "center",
height: 50,
paddingHorizontal: 24,
height: 80,
marginTop: 0,
paddingHorizontal: 12,
position: "absolute",
zIndex: 999,
backgroundColor: "rgba(0,0,0,0.3)",
marginTop: Platform.OS === "android" ? 30 : 0
paddingTop: Platform.OS === "android" ? 30 : 0
}}
>
<IconButton
@@ -163,7 +137,7 @@ const ImagePreview = () => {
)}
imageUrls={[
{
url: image as string
url: image
}
]}
/>

View File

@@ -19,51 +19,38 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
import { useThemeColors } from "@notesnook/theme";
import React from "react";
import { Linking, ScrollView, useWindowDimensions, View } from "react-native";
import {
Linking,
ScrollView,
TouchableOpacity,
useWindowDimensions,
View
} from "react-native";
import { SwiperFlatList } from "react-native-swiper-flatlist";
import { eSendEvent } from "../../services/event-manager";
import Navigation from "../../services/navigation";
import Icon from "react-native-vector-icons/MaterialCommunityIcons";
import { getElevationStyle } from "../../utils/elevation";
import useGlobalSafeAreaInsets from "../../hooks/use-global-safe-area-insets";
import SettingsService from "../../services/settings";
import { useSettingStore } from "../../stores/use-setting-store";
import { getElevationStyle } from "../../utils/elevation";
import { eOpenLoginDialog } from "../../utils/events";
import { SIZE } from "../../utils/size";
import { AuthMode } from "../auth";
import { Button } from "../ui/button";
import Heading from "../ui/typography/heading";
import Paragraph from "../ui/typography/paragraph";
import Navigation from "../../services/navigation";
import { eOpenLoginDialog } from "../../utils/events";
import { AuthMode } from "../auth";
import { eSendEvent } from "../../services/event-manager";
type IntroItem = {
headings?: string[];
body?: string;
testimonial?: string;
link?: string;
user?: string;
};
const introItems: IntroItem[] = [
{
headings: ["Open source.", "End to end encrypted.", "Private."],
body: "Write notes with freedom, no spying, no tracking."
},
{
headings: ["Privacy for everyone", "— not just the", "privileged few"],
body: "Your privacy matters to us, no matter who you are. In a world where everyone is trying to spy on you, Notesnook encrypts all your data before it leaves your device. With Notesnook no one can ever sell your data again."
},
{
testimonial:
"You simply cannot get any better of a note taking app than @notesnook. The UI is clean and slick, it is feature rich, encrypted, reasonably priced (esp. for students & educators) & open source",
link: "https://twitter.com/andrewsayer/status/1637817220113002503",
user: "@andrewsayer on Twitter"
}
];
const Intro = () => {
const Intro = ({ navigation }) => {
const { colors } = useThemeColors();
const { width } = useWindowDimensions();
const isTelemetryEnabled = useSettingStore(
(state) => state.settings.telemetry
);
const { width, height } = useWindowDimensions();
const deviceMode = useSettingStore((state) => state.deviceMode);
const insets = useGlobalSafeAreaInsets();
const renderItem = React.useCallback(
({ item }: { item: IntroItem }) => (
({ item }) => (
<View
style={{
justifyContent: "center",
@@ -118,18 +105,17 @@ const Intro = () => {
{item.body ? <Paragraph size={SIZE.sm}>{item.body}</Paragraph> : null}
{item.testimonial ? (
{item.tesimonial ? (
<Paragraph
style={{
fontStyle: "italic",
fontSize: SIZE.lg
}}
onPress={() => {
if (!item.link) return;
Linking.openURL(item.link);
}}
>
{item.testimonial} {item.user}
{item.tesimonial} {item.user}
</Paragraph>
) : null}
</View>
@@ -144,23 +130,22 @@ const Intro = () => {
style={{
width: "100%"
}}
contentContainerStyle={{
minHeight: "100%"
}}
>
<View
style={{
width: deviceMode !== "mobile" ? width / 2 : "100%",
backgroundColor: colors.secondary.background,
marginBottom: 20,
borderBottomWidth: 1,
borderBottomColor: colors.primary.border,
alignSelf: deviceMode !== "mobile" ? "center" : undefined,
borderWidth: deviceMode !== "mobile" ? 1 : undefined,
borderColor:
deviceMode !== "mobile" ? colors.primary.border : undefined,
borderRadius: deviceMode !== "mobile" ? 20 : undefined,
marginTop: deviceMode !== "mobile" ? 50 : undefined,
flexGrow: 2
borderWidth: deviceMode !== "mobile" ? 1 : null,
borderColor: deviceMode !== "mobile" ? colors.primary.border : null,
borderRadius: deviceMode !== "mobile" ? 20 : null,
marginTop: deviceMode !== "mobile" ? 50 : null,
paddingTop: insets.top + 10,
paddingBottom: insets.top + 10,
minHeight: height * 0.7
}}
>
<SwiperFlatList
@@ -170,7 +155,26 @@ const Intro = () => {
index={0}
useReactNativeGestureHandler={true}
showPagination
data={introItems}
data={[
{
headings: ["Open source.", "End to end encrypted.", "Private."],
body: "Write notes with freedom, no spying, no tracking."
},
{
headings: [
"Privacy for everyone",
"— not just the",
"privileged few"
],
body: "Your privacy matters to us, no matter who you are. In a world where everyone is trying to spy on you, Notesnook encrypts all your data before it leaves your device. With Notesnook no one can ever sell your data again."
},
{
tesimonial:
"You simply cannot get any better of a note taking app than @notesnook. The UI is clean and slick, it is feature rich, encrypted, reasonably priced (esp. for students & educators) & open source",
link: "https://twitter.com/andrewsayer/status/1637817220113002503",
user: "@andrewsayer on Twitter"
}
]}
paginationActiveColor={colors.primary.accent}
paginationStyleItem={{
width: 10,
@@ -186,14 +190,13 @@ const Intro = () => {
<View
style={{
width: "100%",
paddingHorizontal: 16,
gap: 12,
paddingVertical: 16
justifyContent: "center",
minHeight: height * 0.3
}}
>
<Button
width="100%"
onPress={() => {
width={250}
onPress={async () => {
eSendEvent(eOpenLoginDialog, AuthMode.welcomeSignup);
setTimeout(() => {
SettingsService.set({
@@ -207,29 +210,51 @@ const Intro = () => {
style={{
paddingHorizontal: 24,
alignSelf: "center",
...getElevationStyle(5)
...getElevationStyle(5),
borderRadius: 100
}}
fontSize={SIZE.md}
type="accent"
title="Get started"
/>
<Button
width="100%"
title="I already have an account"
type="secondary"
onPress={() => {
eSendEvent(eOpenLoginDialog, AuthMode.login);
setTimeout(() => {
SettingsService.set({
introCompleted: true
});
Navigation.replace("Notes", {
canGoBack: false
});
}, 1000);
<TouchableOpacity
activeOpacity={1}
style={{
flexDirection: "row",
alignSelf: "center",
width: "90%",
marginBottom: 12,
paddingHorizontal: 12,
justifyContent: "center",
padding: 12,
maxWidth: 500
}}
/>
onPress={() => {
SettingsService.set({ telemetry: !isTelemetryEnabled });
}}
>
<Icon
size={SIZE.md}
name={
isTelemetryEnabled ? "checkbox-marked" : "checkbox-blank-outline"
}
color={
isTelemetryEnabled ? colors.primary.accent : colors.primary.icon
}
/>
<Paragraph
style={{
flexShrink: 1,
marginLeft: 6
}}
size={SIZE.xs}
>
Help improve Notesnook by sending completely anonymized{" "}
<Heading size={SIZE.xs}>private analytics and bug reports.</Heading>
</Paragraph>
</TouchableOpacity>
</View>
</ScrollView>
);

File diff suppressed because one or more lines are too long

View File

@@ -19,6 +19,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
import { View } from "react-native";
import React from "react";
export const Footer = ({ height = 150 }) => {
return <View style={{ height: height }} />;
export const Footer = () => {
return <View style={{ height: 150 }} />;
};

View File

@@ -95,7 +95,7 @@ export const NotebookHeader = ({
style={{
flexShrink: 1
}}
size={SIZE.lg}
size={SIZE.xxl}
>
{notebook.title}
</Heading>
@@ -114,7 +114,7 @@ export const NotebookHeader = ({
width: 40,
height: 40
}}
type="transparent"
type={isPinnedToMenu ? "secondary" : "secondary"}
color={isPinnedToMenu ? colors.primary.accent : colors.primary.icon}
size={SIZE.lg}
/>
@@ -123,7 +123,7 @@ export const NotebookHeader = ({
onPress={onEditNotebook}
tooltipText="Edit this notebook"
name="pencil"
type="transparent"
type="secondary"
color={colors.primary.icon}
style={{
width: 40,
@@ -134,7 +134,7 @@ export const NotebookHeader = ({
</View>
{notebook.description ? (
<Paragraph size={SIZE.sm} color={colors.primary.paragraph}>
<Paragraph size={SIZE.md} color={colors.primary.paragraph}>
{notebook.description}
</Paragraph>
) : null}
@@ -148,6 +148,10 @@ export const NotebookHeader = ({
size={SIZE.xs}
color={colors.secondary.paragraph}
>
{/* {notebook.topics.length === 1
? "1 topic"
: `${notebook.topics.length} topics`}
,{" "} */}
{notebook && totalNotes > 1
? totalNotes + " notes"
: totalNotes === 1

View File

@@ -25,7 +25,7 @@ import { useIsCompactModeEnabled } from "../../../hooks/use-is-compact-mode-enab
import { presentSheet } from "../../../services/event-manager";
import SettingsService from "../../../services/settings";
import { RouteName } from "../../../stores/use-navigation-store";
import { getContainerBorder } from "../../../utils/colors";
import { ColorValues, getContainerBorder } from "../../../utils/colors";
import { GROUP } from "../../../utils/constants";
import { SIZE } from "../../../utils/size";
import Sort from "../../sheets/sort";
@@ -96,9 +96,13 @@ export const SectionHeader = React.memo<
}}
>
<Heading
color={color || colors.primary.accent}
color={
ColorValues[color?.toLowerCase() as keyof typeof ColorValues] ||
colors.primary.accent
}
size={SIZE.sm}
style={{
minWidth: 60,
alignSelf: "center",
textAlignVertical: "center"
}}

View File

@@ -211,31 +211,6 @@ const NoteItem = ({
color={colors.error.accent}
/>
) : null}
{item.localOnly ? (
<Icon
testID="sync-off"
name="sync-off"
size={SIZE.sm}
style={{
marginRight: 6
}}
color={primaryColors.icon}
/>
) : null}
{item.readonly ? (
<Icon
testID="pencil-lock"
name="pencil-lock"
size={SIZE.sm}
style={{
marginRight: 6
}}
color={primaryColors.icon}
/>
) : null}
<TimeSince
style={{
fontSize: SIZE.xs,
@@ -251,13 +226,12 @@ const NoteItem = ({
style={{
flexDirection: "row",
alignItems: "center",
marginRight: 6,
gap: 2
marginRight: 6
}}
>
<Icon
name="attachment"
size={SIZE.sm}
size={SIZE.md}
color={primaryColors.icon}
/>
<Paragraph
@@ -297,7 +271,7 @@ const NoteItem = ({
<Icon
testID="icon-star"
name="star-outline"
size={SIZE.sm}
size={SIZE.md}
style={{
marginRight: 6
}}
@@ -314,8 +288,7 @@ const NoteItem = ({
height={23}
type="plain"
textStyle={{
textDecorationLine: "underline",
color: colors.secondary.paragraph
textDecorationLine: "underline"
}}
hitSlop={{ top: 8, bottom: 12, left: 0, right: 0 }}
fontSize={SIZE.xs}
@@ -397,7 +370,7 @@ const NoteItem = ({
<Icon
testID="icon-star"
name="star-outline"
size={SIZE.sm}
size={SIZE.md}
style={{
marginRight: 6
}}

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