Compare commits

..

696 Commits

Author SHA1 Message Date
Ammar Ahmed
5e68dd129e Update android.publish.yml
Signed-off-by: Ammar Ahmed <40239442+ammarahm-ed@users.noreply.github.com>
2024-02-27 01:03:48 +05:00
Ammar Ahmed
2350d0e41d mobile: minor fix 2024-02-27 01:02:39 +05:00
Ammar Ahmed
fbd5c8e3cd mobile: fix github release 2024-02-27 01:01:07 +05:00
Abdullah Atta
ee5c4077fe docs: improve skiff pages import guide 2024-02-13 01:07:14 +05:00
Abdullah Atta
47bbfc618e docs: add guide for migrating from skiff pages 2024-02-12 10:49:40 +05:00
Abdullah Atta
b377329a8b core: fix attachment mime type on read 2024-02-02 16:02:20 +05:00
Abdullah Atta
ae8bcf3865 ci: fix you may only define one of branches and branches-ignore for a single event
Signed-off-by: Abdullah Atta <abdullahatta@streetwriters.co>
2024-01-31 12:46:32 +05:00
Abdullah Atta
2f67611829 ci: add workflows for v3 beta publishing 2024-01-31 12:41:08 +05:00
Ammar Ahmed
058c612f4f ci: update default build tools version android
Signed-off-by: Ammar Ahmed <40239442+ammarahm-ed@users.noreply.github.com>
2024-01-30 11:41:20 +05:00
Ammar Ahmed
be8df90ccb iOS Publish Workflow (#4194)
Signed-off-by: Ammar Ahmed <40239442+ammarahm-ed@users.noreply.github.com>
2024-01-30 11:32:48 +05:00
Abdullah Atta
aa730b5b93 web: fix title only txt exports 2024-01-30 00:29:46 +05:00
Abdullah Atta
34bbcf6387 web: add feature highlights for v2.6.15 2024-01-29 23:33:15 +05:00
Abdullah Atta
33c3c78e2f desktop: get rid of configuration.win.signingHashAlgorithms should be an array 2024-01-29 23:27:45 +05:00
Ammar Ahmed
bf144dcda4 mobile: add ExportOptions.plist 2024-01-29 23:23:48 +05:00
Ammar Ahmed
284ebdb86f ci: @notesnook/ios
Signed-off-by: Ammar Ahmed <40239442+ammarahm-ed@users.noreply.github.com>
2024-01-29 23:23:01 +05:00
Abdullah Atta
9dac4cf7f4 desktop: set signing has to sha256 2024-01-29 23:22:29 +05:00
Abdullah Atta
fb973a5514 ci: run build job no matter what 2024-01-29 23:22:29 +05:00
Abdullah Atta
f9226510f9 desktop: use full path for FilesCatalog 2024-01-29 23:22:29 +05:00
Abdullah Atta
5c0a964699 ci: add inputs to disable build for specific platforms 2024-01-29 23:22:29 +05:00
Abdullah Atta
41ffaab3dc ci: install azurecodesigning module 2024-01-29 23:22:29 +05:00
Abdullah Atta
764c721f1d desktop: add windows code signing 2024-01-29 23:22:29 +05:00
Ammar Ahmed
3d267f91ef mobile: bump version code 2024-01-29 19:55:41 +05:00
Ammar Ahmed
f10f28a7f8 mobile: release v2.6.15 2024-01-29 19:32:14 +05:00
Ammar Ahmed
8d04f89a18 mobile: remove progress bar 2024-01-29 19:06:01 +05:00
Ammar Ahmed
9d1374dcd4 editor: fix progress not showing in image on mobile 2024-01-29 19:05:49 +05:00
Abdullah Atta
094a65e514 editor: hide progress on image if 0 2024-01-29 18:26:44 +05:00
Ammar Ahmed
372da979e1 mobile: support ignoring edits 2024-01-29 18:26:44 +05:00
Abdullah Atta
d3371326f2 editor: add progress indicator to image 2024-01-29 18:26:44 +05:00
Abdullah Atta
57452bd0e6 core: allow setting custom date edited 2024-01-29 18:26:44 +05:00
Abdullah Atta
993b8460bf web: do not update dateEdited if ignoring edits 2024-01-29 18:26:44 +05:00
Abdullah Atta
081201aa42 editor: add support for ignoring edits 2024-01-29 18:26:44 +05:00
Abdullah Atta
270e6778d1 editor: optimize image loading when fast scrolling 2024-01-29 16:23:59 +05:00
Abdullah Atta
2074320583 editor: revoke blob urls on image unmount 2024-01-29 16:09:05 +05:00
Abdullah Atta
cd90030616 web: fix note title in toolbar not changing 2024-01-29 15:45:13 +05:00
Abdullah Atta
807bd683b7 web: do not show success prompt on unlock cancel 2024-01-29 15:36:01 +05:00
Abdullah Atta
35929026a4 web: monograph.notesnook.com > monogr.ph 2024-01-29 15:22:22 +05:00
Ammar Ahmed
41eb94ee47 mobile: fix exporting locked notes 2024-01-29 11:02:32 +05:00
Ammar Ahmed
ba51e110ad mobile: fix opening webclips 2024-01-29 11:02:32 +05:00
Ammar Ahmed
ecad06d249 mobile: disable rtl mode 2024-01-29 11:02:32 +05:00
Abdullah Atta
19a151f637 editor: fix image query matching all images in editor 2024-01-28 17:17:45 +05:00
Ammar Ahmed
7c4a6f1e17 global: update deps 2024-01-27 23:06:24 +05:00
Ammar Ahmed
bc88e953d1 mobile: make sure tools render in toolbar 2024-01-27 23:05:30 +05:00
Ammar Ahmed
7d89ec676a mobile: fix attachments not loading when added from share ext 2024-01-27 22:50:41 +05:00
Ammar Ahmed
c8f8bcca93 mobile: organize bulk exports 2024-01-27 21:23:29 +05:00
Abdullah Atta
9fa904e849 web: fix exporting of single note 2024-01-27 21:23:29 +05:00
Abdullah Atta
05cc947cb4 web: fix pdf exports 2024-01-27 21:23:29 +05:00
alihamuh
a20a683b17 web: code refactoring 2024-01-27 21:23:29 +05:00
alihamuh
5fc43c931e web: added directory paths for exports 2024-01-27 21:23:29 +05:00
Abdullah Atta
0879c3b4d6 core: fix formatting of single spaced paragarphs in markdown exports (#4186)
* core: update showdown library to support single spaced paragraphs

* core: add tests

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

* editor: some corrections

* editor: merge if conditions

---------

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

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

---------

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

* editor: force coerce value to boolean

---------

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

* web: always use zip streams for writing zips

* core: fix types

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

* editor: removed extra import

* editor: update test snapshots

---------

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

* editor: TO BE CONTINUED

* editor: some comments for testing

* editor: comment removed

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

* editor: disable invalid tools when inside code block

* editor: remove unused console.log

---------

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

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

* editor: use Mod key instead of Ctrl

---------

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

* editor: hide editable tools from readonly toolbar

---------

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

* web: fix back navigation for dialogs that replace

* web: remove unused import

---------

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

* web: click should open autosuggestions menu

---------

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

* editor: fix resize for floating images

---------

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

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

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

* web: make attachment filename case insensitive

---------

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

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

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

---------

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

* web: fix font style of checkbox in password dialog

---------

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

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

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

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

* editor: fixed tasklist title input

* editor: allow write protecting nested task lists

---------

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

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

* editor: refactor attachment dragging check

---------

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

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

* editor: improve paragraph empty check

* editor: add tests

* editor: fix CRLF line breaks when pasting text

---------

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

* web: removed regex and added replaceAll in loop

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

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

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

---------

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

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

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

* themebuilder: update theme docs

* docs: finalize docs for custom themes

---------

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

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

* mobile: add script to tree shake icon font file

* mobile: fix icon loading

* mobile: remove html-entities dep

* mobile: add missing icon fonts

* mobile: include plain editor

* mobile: add missing fonts

* mobile: use webpack-bundle

* mobile: keep generated fonts in repo

* mobile: update fonts

* mobile: fix duplicate key warning

* mobile: update fonts

* mobile: disable gesure on reminder sheet

* mobile: update fonts

* mobile: reset session correctly on logout

* mobile: update icon fonts

* mobile: set button action on reminder sheet

* mobile: add missing icons

* mobile: fix crash

* mobile: fix right menus

* mobile: remove console.log

* mobile: disable bounce effect

* mobile: update deps

---------

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

* theme: add theme engine

* mobile: migrate app colors to new theme engine

* mobile: fixed some colors

* mobile: fix colors

* mobile: store theme info in store

* theme: `ColorsType` -> `Variants`

* theme: use explicit return type for `useThemeColors`

* theme: add `backdrop` color

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

* theme: add default pitch-black theme

* mobile: manage theme state via theme-engine

* mobile: add theme scopes

* mobile: commit

* mobile: fix button width on applock screen

* mobile: fix typings

* mobile: fix theme definition

* web: add partial support for custom themes

only context menus & popups are left.

* theme: add dialog & sheet scopes

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

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

* mobile: fix colors

* mobile: fix colors

* mobile: cleanup

* mobile: fix status bar color incorrect on entering foreground

* mobile: fix dark color scheme

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

* editor: add support for theme enging

* web: adjust hover & focus colors on list item

* mobile: migrate share ext to theme engine

* mobile: fix editor theme provider

* clipper: add support for the new theme engine

* mobile: fix statusbar color on switch from bg

* misc: fix build

* mobile: fix build

* misc: fix colors

* mobile: fix theme colors

* mobile: fix bottom padding

* server: add theme server

* theme: add previewColors

* server: support themes query pagination

* mobile: add client from theme server

* server: reset cache on sync repo

* server: fix types

* server: show ip & port on start server

* server: theme updates

* web: finalize new theme engine on web

* editor: fix build

* global: fix @emotion/react version to 11.11.1

* editor: update katex patch

* web: fix imports

* global: fix @trpc/* versions

* global: a huge set of changes

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

* web: add support for theme search and theme switching

* global: update lockfiles

* mobile: fix error

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

* web: add support for auto updating themes

* mobile: update theme selector

* mobile: update theme if new verison available

* theme: add `isomorphic-fetch` package

* global: update lockfiles

* web: add theme details dialog

* setup: add scope for themes server in bootstrap script

* web: add production server url

* web: update lockfile

* web: update lockfile

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

* web: add support for endless scrolling in themes

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

* web: fix colors in places

* theme: add selected variant

* global: use single typescript version across the projects

* web: fix sort & group options not having submenus

* web: apply selected variant where appropriate

* ui: use unique id for all menu items

* config: add ui scope for commits

* theme: export button variant creation fn

* web: fix only 1 theme showing in theme selector

* web: fix navigation item hover & other colors

* mobile: update theme

* editor: fix toolbar group alignments

* editor: set theme provider at app level

* theme: use scope name to get current scope

* mobile: fix color usage in message card

* theme: remove caching

* editor: bring back icons in table menus

* theme: use zustand to manage theme engine state

* web: fix login/signup theming

* mobile: fix webpack build

* misc: remove ThemeProvider usage

* editor: adjust theming and styling of editor toolbar

* mobile: refactor

* editor: fix toolbar group padding everywhere

* web: fix settings sidebar is not scrollable

* web: add loading indicator for themes loading

* mobile: fix warning

* mobile: fix ui issues

* web: fix Loader errors on build

* theme: add getPreviewColors & validateTheme

* theme: fix theme validation

* mobile: load theme from file

* mobile: fix share extension crash

* mobile: rename state

* theme: add sourceURL property

* theme: refactor theme-engine

* web: add support for loading theme from file

* web: improve button hover interaction

* mobile: fix floating button color

* mobile: update theme

* mobile: fix border radius of context menu

* mobile: set sheet overlay color to theme backdrop

* mobile: set sidemenu backdrop to theme backdrop

---------

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

View File

@@ -14,6 +14,7 @@ const SCOPES = [
"mobile",
"web",
"vericrypt",
"desktop",
"crypto",
"editor",
@@ -32,7 +33,8 @@ const SCOPES = [
"misc",
"common",
"global",
"docs"
"docs",
"themebuilder"
];
module.exports = {

View File

@@ -19,7 +19,6 @@ runs:
packages/crypto/package-lock.json
packages/sodium/package-lock.json
packages/clipper/package-lock.json
packages/crypto-worker/package-lock.json
packages/editor-mobile/package-lock.json
packages/editor/package-lock.json
packages/logger/package-lock.json

View File

@@ -1,4 +1,4 @@
name: Publish @notesnook/mobile
name: Publish @notesnook/android
on: workflow_dispatch
@@ -28,30 +28,6 @@ jobs:
- name: Make Gradlew Executable
run: cd apps/mobile/native/android && chmod +x ./gradlew
- name: Build unsigned app bundle
run: yarn release:android:bundle
- name: Sign app bundle for Playstore release
id: sign_app
uses: r0adkll/sign-android-release@v1
with:
releaseDirectory: apps/mobile/native/android/app/build/outputs/bundle/release
signingKeyBase64: ${{ secrets.SIGNING_KEY }}
alias: ${{ secrets.ALIAS }}
keyStorePassword: ${{ secrets.KEY_PASSWORD }}
keyPassword: ${{ secrets.KEY_PASSWORD }}
- name: Publish to Playstore
id: deploy
uses: r0adkll/upload-google-play@v1.1.1
with:
serviceAccountJsonPlainText: ${{ secrets.SERVICE_ACCOUNT_JSON }}
packageName: com.streetwriters.notesnook
releaseFiles: ${{steps.sign_app.outputs.signedReleaseFile}}
track: production
status: completed
whatsNewDirectory: apps/mobile/native/android/releasenotes/
- name: Build apks for Github release
run: yarn release:android
@@ -64,6 +40,8 @@ jobs:
alias: ${{ secrets.PUBLIC_ALIAS }}
keyStorePassword: ${{ secrets.PUBLIC_KEY_PASSWORD }}
keyPassword: ${{ secrets.PUBLIC_KEY_PASSWORD }}
env:
BUILD_TOOLS_VERSION: "33.0.0"
- name: Rename apk files
run: |

View File

@@ -18,6 +18,21 @@ on:
required: true
default: true
description: "Publish on GitHub releases?"
build-windows:
type: boolean
required: true
default: true
description: "Build for Windows?"
build-linux:
type: boolean
required: true
default: true
description: "Build for Linux?"
build-mac:
type: boolean
required: true
default: true
description: "Build for macOS?"
jobs:
build:
@@ -71,6 +86,7 @@ jobs:
build-macos:
name: Build for macOS
needs: build
if: inputs.build-mac
runs-on: macos-12
steps:
@@ -97,6 +113,7 @@ jobs:
- name: Get App Store Version
id: appstore
uses: streetwriters/appstore-connect-app-version@develop
if: inputs.publish-apple
with:
app-id: ${{ steps.app_metadata.outputs.apple_app_id }}
key-id: ${{ secrets.api_key_id }}
@@ -162,6 +179,7 @@ jobs:
build-linux:
name: Build for Linux
needs: build
if: inputs.build-linux
runs-on: ubuntu-22.04
steps:
@@ -220,12 +238,22 @@ jobs:
build-windows:
name: Build for Windows
needs: build
if: inputs.build-windows
runs-on: windows-latest
steps:
- name: Check out Git repository
uses: actions/checkout@v3
- name: Setup .NET Core SDK
uses: actions/setup-dotnet@v2
with:
dotnet-version: 6.0.x
- name: Install AzureCodeSigning
run: Install-Module -Name AzureCodeSigning -RequiredVersion 0.3.0 -Force -Repository PSGallery
shell: pwsh
- name: Setup Node
uses: ./.github/actions/setup-node-with-cache
@@ -246,6 +274,9 @@ jobs:
- name: Publish
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }}
AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }}
AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }}
run: |
if ($${{ inputs.publish-github }} -eq $true) {
yarn electron-builder --win --publish always

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

@@ -0,0 +1,67 @@
name: Publish @notesnook/ios
on: workflow_dispatch
jobs:
build:
runs-on: macos-13
timeout-minutes: 60
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Setup Node
uses: ./.github/actions/setup-node-with-cache
- name: Install node modules
run: |
npm ci --ignore-scripts --prefer-offline --no-audit
npm run bootstrap -- --scope=mobile
- name: Build packages
run: npx nx run @notesnook/mobile:build
- name: Install Pods
run: npm run prepare:ios
- name: Build iOS App
uses: yukiarrr/ios-build-action@v1.11.2
with:
bundle-identifier: org.streetwriters.notesnook
scheme: Notesnook
configuration: "Release"
export-options: apps/mobile/native/ios/ExportOptions.plist
project-path: apps/mobile/native/ios/Notesnook.xcodeproj
workspace-path: apps/mobile/native/ios/Notesnook.xcworkspace
update-targets: |
Notesnook
Make Note
NotesWidgetExtension
disable-targets: Notesnook-tvOS,Notesnook-tvOSTests,NotesnookTests
code-signing-identity: Apple Distribution
team-id: ${{ secrets.APPLE_TEAM_ID }}
p12-base64: ${{ secrets.APPLE_CERTIFICATE_P12 }}
certificate-password: ${{ secrets.APPLE_CERTIFICATE_P12_PASSWORD }}
app-store-connect-api-key-issuer-id: ${{ secrets.API_KEY_ISSUER_ID }}
app-store-connect-api-key-id: ${{ secrets.APPSTORE_KEY_ID }}
app-store-connect-api-key-base64: ${{ secrets.APPSTORE_CONNECT_API_KEY_BASE64 }}
output-path: Notesnook.ipa
mobileprovision-base64: |
${{ secrets.APPLE_MOBILE_PROVISION_APP }}
${{ secrets.APPLE_MOBILE_PROVISION_SHARE }}
${{ secrets.APPLE_MOBILE_PROVISION_WIDGET }}
- name: 'Upload app to TestFlight'
uses: apple-actions/upload-testflight-build@v1
with:
app-path: Notesnook.ipa
issuer-id: ${{ secrets.API_KEY_ISSUER_ID }}
api-key-id: ${{ secrets.API_KEY_ID }}
api-private-key: ${{ secrets.API_KEY }}
- name: Upload to Github
uses: actions/upload-artifact@v2
with:
name: Notesnook.ipa
path: Notesnook.ipa

View File

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

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

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

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

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

View File

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

View File

@@ -0,0 +1,40 @@
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: 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-v3-app
directory: ./apps/web/build
github-token: ${{ secrets.GITHUB_TOKEN }}
github-environment: ${{ (github.ref == 'refs/heads/master' && 'beta') || 'preview' }}

3
.gitignore vendored
View File

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

View File

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

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

@@ -2,7 +2,7 @@
"name": "@notesnook/desktop",
"productName": "Notesnook",
"description": "Your private note taking space",
"version": "2.5.8",
"version": "2.6.15",
"appAppleId": "1544027013",
"private": true,
"main": "./dist/index.js",
@@ -12,25 +12,25 @@
"license": "GPL-3.0-or-later",
"dependencies": {
"@notesnook/crypto": "file:../../packages/crypto",
"@trpc/client": "10.31.0",
"@trpc/server": "10.31.0",
"@trpc/client": "10.38.3",
"@trpc/server": "10.38.3",
"electron-trpc": "0.5.2",
"electron-updater": "6.1.1",
"electron-updater": "6.1.4",
"icojs": "^0.17.1",
"typed-emitter": "^2.1.0",
"yargs": "^17.6.2",
"zod": "^3.21.4"
},
"devDependencies": {
"@types/node": "^18.15.0",
"@types/node": "18.16.1",
"@types/yargs": "^17.0.24",
"chokidar": "^3.5.3",
"electron": "24.5.1",
"electron-builder": "24.4.0",
"electron": "25.9.8",
"electron-builder": "^24.9.1",
"electron-builder-notarize": "^1.5.1",
"esbuild": "^0.17.19",
"node-fetch": "^3.3.1",
"tree-kill": "^1.2.2"
"tree-kill": "^1.2.2",
"undici": "^5.23.0"
},
"optionalDependencies": {
"dmg-license": "^1.0.11"
@@ -135,6 +135,10 @@
]
}
],
"signingHashAlgorithms": [
"sha256"
],
"sign": "./sign.js",
"icon": "assets/icons/app.ico"
},
"portable": {
@@ -191,6 +195,7 @@
"allowNativeWayland": true
},
"extraResources": [
"app-update.yml",
"./assets/**"
],
"extraMetadata": {

View File

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

60
apps/desktop/sign.js Normal file
View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -31,7 +31,6 @@ declare global {
}
process.once("loaded", async () => {
console.log("HELLO!");
const electronTRPC: RendererGlobalElectronTRPC = {
sendMessage: (operation) =>
ipcRenderer.send(ELECTRON_TRPC_CHANNEL, operation),

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -16,12 +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 "@azure/core-asynciterator-polyfill";
import {
THEME_COMPATIBILITY_VERSION,
useThemeEngineStore
} from "@notesnook/theme";
import React, { useEffect } from "react";
import { View } from "react-native";
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";
@@ -30,14 +31,17 @@ import { withErrorBoundry } from "./components/exception-handler";
import GlobalSafeAreaProvider from "./components/globalsafearea";
import { useAppEvents } from "./hooks/use-app-events";
import { ApplicationHolder } from "./navigation";
import { themeTrpcClient } from "./screens/settings/theme-selector";
import Notifications from "./services/notifications";
import SettingsService from "./services/settings";
import { TipManager } from "./services/tip-manager";
import { useThemeStore } from "./stores/use-theme-store";
import { useUserStore } from "./stores/use-user-store";
import { themeTrpcClient } from "./screens/settings/theme-selector";
SettingsService.init();
I18nManager.allowRTL(false);
I18nManager.forceRTL(false);
I18nManager.swapLeftAndRightInRTL(false);
SettingsService.checkOrientation();
const App = () => {
const init = useAppEvents();
@@ -46,6 +50,7 @@ const App = () => {
if (appLockMode && appLockMode !== "none") {
useUserStore.getState().lockApp(true);
}
globalThis["IS_MAIN_APP_RUNNING"] = true;
init();
setTimeout(async () => {
SettingsService.onFirstLaunch();
@@ -93,6 +98,12 @@ const App = () => {
);
};
let currTheme =
useThemeStore.getState().colorScheme === "dark"
? SettingsService.getProperty("darkTheme")
: SettingsService.getProperty("lightTheme");
useThemeEngineStore.getState().setTheme(currTheme);
export const withTheme = (Element) => {
return function AppWithThemeProvider() {
const [colorScheme, darkTheme, lightTheme] = useThemeStore((state) => [
@@ -102,31 +113,35 @@ export const withTheme = (Element) => {
]);
useEffect(() => {
const currentTheme = colorScheme === "dark" ? darkTheme : lightTheme;
if (!currentTheme) return;
themeTrpcClient.updateTheme
.query({
version: currentTheme.version,
compatibilityVersion: THEME_COMPATIBILITY_VERSION,
id: currentTheme.id
})
.then((theme) => {
if (theme) {
console.log(theme.version, "theme updated");
theme.colorScheme === "dark"
? useThemeStore.getState().setDarkTheme(theme)
: useThemeStore.getState().setLightTheme(theme);
}
})
.catch(console.log);
setTimeout(() => {
const currentTheme = colorScheme === "dark" ? darkTheme : lightTheme;
if (!currentTheme) return;
themeTrpcClient.updateTheme
.query({
version: currentTheme.version,
compatibilityVersion: THEME_COMPATIBILITY_VERSION,
id: currentTheme.id
})
.then((theme) => {
if (theme) {
console.log(theme.version, "theme updated");
theme.colorScheme === "dark"
? useThemeStore.getState().setDarkTheme(theme)
: useThemeStore.getState().setLightTheme(theme);
}
})
.catch(console.log);
}, 1000);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
useEffect(() => {
const nextTheme = colorScheme === "dark" ? darkTheme : lightTheme;
if (JSON.stringify(nextTheme) !== JSON.stringify(currTheme)) {
useThemeEngineStore
.getState()
.setTheme(colorScheme === "dark" ? darkTheme : lightTheme);
}, [colorScheme, darkTheme, lightTheme]);
currTheme = nextTheme;
}
return <Element />;
};

View File

@@ -126,6 +126,18 @@ export async function decrypt(password, data) {
return await Sodium.decrypt(password, _data);
}
export async function decryptMulti(password, data) {
if (!password.password && !password.key) return undefined;
if (password.password && password.password === "" && !password.key)
return undefined;
data = data.map((d) => {
d.output = "plain";
return d;
});
return await Sodium.decryptMulti(password, data);
}
export function parseAlgorithm(alg) {
if (!alg) return {};
const [enc, kdf, compressed, compressionAlg, base64variant] = alg.split("-");
@@ -154,3 +166,24 @@ export async function encrypt(password, data) {
alg: getAlgorithm(7)
};
}
export async function encryptMulti(password, data) {
if (!password.password && !password.key) return undefined;
if (password.password && password.password === "" && !password.key)
return undefined;
let results = await Sodium.encryptMulti(
password,
data.map((item) => ({
type: "plain",
data: item
}))
);
return !results
? []
: results.map((result) => ({
...result,
alg: getAlgorithm(7)
}));
}

View File

@@ -17,56 +17,47 @@ 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 { database } from "@notesnook/common";
import { initalize, logger as dbLogger } from "@notesnook/core/logger";
import { logger as dbLogger } from "@notesnook/core/dist/logger";
import { Platform } from "react-native";
import { MMKVLoader } from "react-native-mmkv-storage";
import filesystem from "../filesystem";
import * as Gzip from "react-native-gzip";
import EventSource from "../../utils/sse/even-source-ios";
import AndroidEventSource from "../../utils/sse/event-source";
import Storage, { KV } from "./storage";
import * as Gzip from "react-native-gzip";
import filesystem from "../filesystem";
import "./logger";
import Storage from "./storage";
const LoggerStorage = new MMKVLoader()
.withInstanceID("notesnook_logs")
.initialize();
database.host(
__DEV__
? {
API_HOST: "https://api.notesnook.com",
AUTH_HOST: "https://auth.streetwriters.co",
SSE_HOST: "https://events.streetwriters.co",
SUBSCRIPTIONS_HOST: "https://subscriptions.streetwriters.co",
ISSUES_HOST: "https://issues.streetwriters.co"
// API_HOST: "http://192.168.43.108:5264",
// AUTH_HOST: "http://192.168.43.108:8264",
// SSE_HOST: "http://192.168.43.108:7264",
// SUBSCRIPTIONS_HOST: "http://192.168.43.108:9264",
// ISSUES_HOST: "http://192.168.43.108:2624"
}
: {
API_HOST: "https://api.notesnook.com",
AUTH_HOST: "https://auth.streetwriters.co",
SSE_HOST: "https://events.streetwriters.co",
SUBSCRIPTIONS_HOST: "https://subscriptions.streetwriters.co",
ISSUES_HOST: "https://issues.streetwriters.co"
}
);
database.host(
__DEV__
? {
API_HOST: "https://api.notesnook.com",
AUTH_HOST: "https://auth.streetwriters.co",
SSE_HOST: "https://events.streetwriters.co",
SUBSCRIPTIONS_HOST: "https://subscriptions.streetwriters.co",
ISSUES_HOST: "https://issues.streetwriters.co"
// API_HOST: "http://192.168.8.101:5264",
// AUTH_HOST: "http://192.168.8.101:8264",
// SSE_HOST: "http://192.168.8.101:7264",
// SUBSCRIPTIONS_HOST: "http://192.168.8.101:9264",
// ISSUES_HOST: "http://192.168.8.101:2624"
}
: {
API_HOST: "https://api.notesnook.com",
AUTH_HOST: "https://auth.streetwriters.co",
SSE_HOST: "https://events.streetwriters.co",
SUBSCRIPTIONS_HOST: "https://subscriptions.streetwriters.co",
ISSUES_HOST: "https://issues.streetwriters.co"
}
);
database.setup(
Storage,
Platform.OS === "ios" ? EventSource : AndroidEventSource,
filesystem,
{
compress: Gzip.deflate,
decompress: Gzip.inflate
}
);
initalize(new KV(LoggerStorage), true);
database.setup(
Storage,
Platform.OS === "ios" ? EventSource : AndroidEventSource,
filesystem,
{
compress: Gzip.deflate,
decompress: Gzip.inflate
}
);
export const db = database;
export const DatabaseLogger = dbLogger;

View File

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

View File

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

View File

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

View File

@@ -22,7 +22,7 @@ import {
getFileNameWithExtension,
isImage,
isDocument
} from "@notesnook/core/utils/filename";
} from "@notesnook/core/dist/utils/filename";
import React from "react";
import { Platform } from "react-native";
import * as ScopedStorage from "react-native-scoped-storage";
@@ -34,6 +34,8 @@ import { useAttachmentStore } from "../../stores/use-attachment-store";
import { db } from "../database";
import Storage from "../database/storage";
import { cacheDir, copyFileAsync, releasePermissions } from "./utils";
import { createCacheDir, exists } from "./io";
import { IOS_APPGROUPID } from "../../utils/constants";
export const FileDownloadStatus = {
Success: 1,
@@ -62,6 +64,7 @@ export async function downloadAttachments(
canceled,
groupId
) {
await createCacheDir();
if (!attachments || !attachments.length) return;
const result = new Map();
@@ -179,9 +182,13 @@ export default async function downloadAttachment(
silent: false,
cache: false,
throwError: false,
groupId: undefined
groupId: undefined,
base64: false,
text: false
}
) {
await createCacheDir();
let attachment = db.attachments.attachment(hash);
if (!attachment) {
console.log("attachment not found");
@@ -208,10 +215,17 @@ export default async function downloadAttachment(
options.groupId || attachment.metadata.hash,
attachment.metadata.hash
);
if (
!(await RNFetchBlob.fs.exists(`${cacheDir}/${attachment.metadata.hash}`))
)
if (!(await exists(attachment.metadata.hash))) {
return;
}
if (options.base64 || options.text) {
return await db.attachments.read(
attachment.metadata.hash,
options.base64 ? "base64" : "text"
);
}
let filename = getFileNameWithExtension(
attachment.metadata.filename,
@@ -229,7 +243,8 @@ export default async function downloadAttachment(
mime: attachment.metadata.type,
fileName: options.cache ? undefined : filename,
uri: options.cache ? undefined : folder.uri,
chunkSize: attachment.chunkSize
chunkSize: attachment.chunkSize,
appGroupId: IOS_APPGROUPID
};
let fileUri = await Sodium.decryptFile(

View File

@@ -17,22 +17,25 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import hosts from "@notesnook/core/utils/constants";
import hosts from "@notesnook/core/dist/utils/constants";
import NetInfo from "@react-native-community/netinfo";
import RNFetchBlob from "react-native-blob-util";
import { ToastEvent } from "../../services/event-manager";
import { useAttachmentStore } from "../../stores/use-attachment-store";
import { db } from "../database";
import { cacheDir, fileCheck } from "./utils";
import { createCacheDir, exists } from "./io";
export async function downloadFile(filename, data, cancelToken) {
if (!data) return false;
let { url, headers } = data;
await createCacheDir();
let { url, headers } = data;
let path = `${cacheDir}/${filename}`;
try {
let exists = await RNFetchBlob.fs.exists(path);
if (exists) {
if (await exists(filename)) {
return true;
}
@@ -60,44 +63,55 @@ export async function downloadFile(filename, data, cancelToken) {
console.log("downloading: ", recieved, total);
});
cancelToken.cancel = request.cancel;
cancelToken.cancel = () => {
useAttachmentStore.getState().remove(filename);
request.cancel();
};
let response = await request;
await fileCheck(response, totalSize);
let status = response.info().status;
useAttachmentStore.getState().remove(filename);
return status >= 200 && status < 300;
} catch (e) {
ToastEvent.show({
heading: "Error downloading file",
message: e.message,
type: "error",
context: "global"
});
ToastEvent.show({
heading: "Error downloading file",
message: e.message,
type: "error",
context: "local"
});
if (e.message !== "canceled") {
ToastEvent.show({
heading: "Error downloading file",
message: e.message,
type: "error",
context: "global"
});
ToastEvent.show({
heading: "Error downloading file",
message: e.message,
type: "error",
context: "local"
});
}
useAttachmentStore.getState().remove(filename);
RNFetchBlob.fs.unlink(path).catch(console.log);
console.log("download file error: ", e, url, headers);
console.log("Download file error:", e, url, headers);
return false;
}
}
export async function getUploadedFileSize(hash) {
const url = `${hosts.API_HOST}/s3?name=${hash}`;
const token = await db.user.tokenManager.getAccessToken();
try {
const url = `${hosts.API_HOST}/s3?name=${hash}`;
const token = await db.user.tokenManager.getAccessToken();
const attachmentInfo = await fetch(url, {
method: "HEAD",
headers: { Authorization: `Bearer ${token}` }
});
const attachmentInfo = await fetch(url, {
method: "HEAD",
headers: { Authorization: `Bearer ${token}` }
});
const contentLength = parseInt(attachmentInfo.headers?.get("content-length"));
return isNaN(contentLength) ? 0 : contentLength;
const contentLength = parseInt(
attachmentInfo.headers?.get("content-length")
);
return isNaN(contentLength) ? 0 : contentLength;
} catch (e) {
return 0;
}
}
export async function checkAttachment(hash) {

View File

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

View File

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

View File

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

View File

@@ -128,7 +128,9 @@ export const Cta = ({ actions, style = {}, color, inline }) => {
fontSize={SIZE.md}
buttonType={{
color: color ? color : colors.primary.accent,
text: colors.static.white,
text: color
? colors.static.white
: colors.primary.accentForeground,
selected: color ? color : colors.primary.accent,
opacity: 1
}}

View File

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

View File

@@ -100,7 +100,7 @@ const AppLockedOverlay = () => {
appLocked && (
<View
style={{
backgroundColor: colors.bg,
backgroundColor: colors.primary.background,
width: "100%",
height: "100%",
position: "absolute",
@@ -160,7 +160,7 @@ const AppLockedOverlay = () => {
style={{
width: "100%",
padding: 12,
backgroundColor: colors.bg
backgroundColor: colors.primary.background
}}
>
{user ? (

View File

@@ -48,7 +48,7 @@ import Heading from "../ui/typography/heading";
import Paragraph from "../ui/typography/paragraph";
import { formatBytes } from "@notesnook/common";
const Actions = ({ attachment, setAttachments, fwdRef }) => {
const Actions = ({ attachment, setAttachments, fwdRef, close }) => {
const { colors } = useThemeColors();
const contextId = attachment.metadata.hash;
const [filename, setFilename] = useState(attachment.metadata.filename);
@@ -64,7 +64,7 @@ const Actions = ({ attachment, setAttachments, fwdRef }) => {
name: "Download",
onPress: async () => {
if (currentProgress) {
await db.fs.cancel(attachment.metadata.hash, "download");
await db.fs.cancel(attachment.metadata.hash);
useAttachmentStore.getState().remove(attachment.metadata.hash);
}
downloadAttachment(attachment.metadata.hash, false);
@@ -117,7 +117,7 @@ const Actions = ({ attachment, setAttachments, fwdRef }) => {
});
}
setAttachments([...db.attachments.all]);
setAttachments();
setLoading({
name: null
});
@@ -140,7 +140,7 @@ const Actions = ({ attachment, setAttachments, fwdRef }) => {
filename: value
});
setFilename(value);
setAttachments([...db.attachments.all]);
setAttachments();
}
},
positiveText: "Rename"
@@ -152,8 +152,8 @@ const Actions = ({ attachment, setAttachments, fwdRef }) => {
name: "Delete",
onPress: async () => {
await db.attachments.remove(attachment.metadata.hash, false);
setAttachments([...db.attachments.all]);
eSendEvent(eCloseSheet, contextId);
setAttachments();
close();
},
icon: "delete-outline"
}
@@ -362,8 +362,13 @@ const Actions = ({ attachment, setAttachments, fwdRef }) => {
Actions.present = (attachment, set, context) => {
presentSheet({
context: context,
component: (ref) => (
<Actions fwdRef={ref} setAttachments={set} attachment={attachment} />
component: (ref, close) => (
<Actions
fwdRef={ref}
setAttachments={set}
close={close}
attachment={attachment}
/>
)
});
};

View File

@@ -40,7 +40,7 @@ import {
isDocument,
isImage,
isVideo
} from "@notesnook/core/utils/filename";
} from "@notesnook/core/dist/utils/filename";
import { useSettingStore } from "../../stores/use-setting-store";
import { FlashList } from "react-native-actions-sheet/dist/src/views/FlashList";
@@ -68,7 +68,7 @@ export const AttachmentDialog = ({ note }) => {
!attachmentSearchValue.current ||
attachmentSearchValue.current === ""
) {
setAttachments([...attachments]);
setAttachments(filterAttachments(currentFilter));
}
clearTimeout(searchTimer.current);
searchTimer.current = setTimeout(() => {
@@ -77,13 +77,16 @@ export const AttachmentDialog = ({ note }) => {
attachmentSearchValue.current
);
if (results.length === 0) return;
setAttachments(results);
setAttachments(filterAttachments(currentFilter, results));
}, 300);
};
const renderItem = ({ item }) => (
<AttachmentItem
setAttachments={setAttachments}
setAttachments={() => {
setAttachments(filterAttachments(currentFilter));
}}
attachment={item}
context="attachments-list"
/>
@@ -133,11 +136,12 @@ export const AttachmentDialog = ({ note }) => {
}
];
const filterAttachments = (type) => {
const attachments = note
? db.attachments.ofNote(note.id, "all")
: [...(db.attachments.all || [])];
isDocument;
const filterAttachments = (type, _attachments) => {
const attachments =
_attachments || note
? db.attachments.ofNote(note.id, "all")
: [...(db.attachments.all || [])];
switch (type) {
case "all":
return attachments;
@@ -241,7 +245,7 @@ export const AttachmentDialog = ({ note }) => {
width: "100%",
height: 50,
flexDirection: "row",
backgroundColor: colors.bg
backgroundColor: colors.primary.background
}}
contentContainerStyle={{
minWidth: "100%",

View File

@@ -17,14 +17,16 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { useThemeColors } from "@notesnook/theme";
import React, { useEffect, useRef, useState } from "react";
import { Platform, View } from "react-native";
import { KeyboardAwareScrollView } from "react-native-keyboard-aware-scroll-view";
import useGlobalSafeAreaInsets from "../../hooks/use-global-safe-area-insets";
import {
eSubscribeEvent,
eUnSubscribeEvent
} from "../../services/event-manager";
import { useThemeColors } from "@notesnook/theme";
import { useUserStore } from "../../stores/use-user-store";
import { eCloseLoginDialog, eOpenLoginDialog } from "../../utils/events";
import { sleep } from "../../utils/time";
import BaseDialog from "../dialog/base-dialog";
@@ -34,7 +36,6 @@ import { IconButton } from "../ui/icon-button";
import { hideAuth, initialAuthMode } from "./common";
import { Login } from "./login";
import { Signup } from "./signup";
import { KeyboardAwareScrollView } from "react-native-keyboard-aware-scroll-view";
export const AuthMode = {
login: 0,
@@ -68,6 +69,9 @@ const AuthModal = () => {
}
const close = () => {
useUserStore.setState({
disableAppLockRequests: false
});
actionSheetRef.current?.hide();
setCurrentAuthMode(AuthMode.login);
setVisible(false);
@@ -77,6 +81,11 @@ const AuthModal = () => {
<BaseDialog
overlayOpacity={0}
statusBarTranslucent={false}
onShow={() => {
useUserStore.setState({
disableAppLockRequests: true
});
}}
onRequestClose={currentAuthMode !== AuthMode.welcomeSignup && close}
visible={true}
onClose={close}
@@ -85,11 +94,14 @@ const AuthModal = () => {
background={colors.primary.background}
transparent={false}
animated={false}
centered={false}
enableSheetKeyboardHandler
>
<KeyboardAwareScrollView
style={{
width: "100%"
}}
enableAutomaticScroll={false}
keyboardShouldPersistTaps="handled"
>
{currentAuthMode !== AuthMode.login ? (

View File

@@ -17,18 +17,22 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { useThemeColors } from "@notesnook/theme";
import React, { useEffect, useState } from "react";
import { Modal, View } from "react-native";
import { db } from "../../common/database";
import { MMKV } from "../../common/database/mmkv";
import BiometricService from "../../services/biometrics";
import {
ToastEvent,
eSendEvent,
eSubscribeEvent
} from "../../services/event-manager";
import { setLoginMessage } from "../../services/message";
import SettingsService from "../../services/settings";
import Sync from "../../services/sync";
import { useThemeColors } from "@notesnook/theme";
import { clearAllStores } from "../../stores";
import { useUserStore } from "../../stores/use-user-store";
import { eLoginSessionExpired, eUserLoggedIn } from "../../utils/events";
import { SIZE } from "../../utils/size";
import { sleep } from "../../utils/time";
@@ -42,6 +46,7 @@ import Input from "../ui/input";
import Heading from "../ui/typography/heading";
import Paragraph from "../ui/typography/paragraph";
import { LoginSteps, useLogin } from "./use-login";
import BaseDialog from "../dialog/base-dialog";
function getObfuscatedEmail(email) {
if (!email) return "";
@@ -64,16 +69,20 @@ export const SessionExpired = () => {
eSendEvent(eUserLoggedIn, true);
setVisible(false);
setFocused(false);
}
},
true
);
const logout = async () => {
try {
await db.user.logout();
await BiometricService.resetCredentials();
SettingsService.set({
introCompleted: true
});
setLoginMessage();
SettingsService.resetSettings();
useUserStore.getState().setUser(null);
useUserStore.getState().setSyncing(false);
MMKV.clearStore();
clearAllStores();
setVisible(false);
} catch (e) {
ToastEvent.show({
@@ -123,15 +132,20 @@ export const SessionExpired = () => {
return (
visible && (
<Modal
<BaseDialog
transparent={false}
background={colors.primary.background}
bounce={false}
animated={false}
centered={false}
onShow={async () => {
await sleep(300);
passwordInputRef.current?.focus();
setFocused(true);
}}
enableSheetKeyboardHandler={true}
visible={true}
>
<SheetProvider context="two_factor_verify" />
<View
style={{
width: focused ? "100%" : "99.9%",
@@ -224,7 +238,9 @@ export const SessionExpired = () => {
</View>
<Toast context="local" />
<Dialog context="session_expiry" />
</Modal>
<SheetProvider context="two_factor_verify" />
</BaseDialog>
)
);
};

View File

@@ -67,6 +67,7 @@ export const Signup = ({ changeMode, trial }) => {
const signup = async () => {
if (!validateInfo() || error) return;
if (loading) return;
setLoading(true);
try {
await db.user.signup(email.current.toLowerCase(), password.current);

View File

@@ -214,6 +214,7 @@ const TwoFactorVerification = ({ onMfaLogin, mfaInfo }) => {
code.current = value;
//onNext();
}}
caretHidden
inputStyle={{
fontSize: SIZE.lg,
height: 60,

View File

@@ -33,7 +33,7 @@ export const LoginSteps = {
passwordAuth: 3
};
export const useLogin = (onFinishLogin) => {
export const useLogin = (onFinishLogin, sessionExpired = false) => {
const [error, setError] = useState(false);
const [loading, setLoading] = useState(false);
const setUser = useUserStore((state) => state.setUser);
@@ -64,10 +64,12 @@ export const useLogin = (onFinishLogin) => {
const login = async () => {
if (!validateInfo() || error) return;
try {
if (loading) return;
setLoading(true);
switch (step) {
case LoginSteps.emailAuth: {
const mfaInfo = await db.user.authenticateEmail(email.current);
console.log("email auth", mfaInfo);
if (mfaInfo) {
TwoFactorVerification.present(async (mfa, callback) => {
try {
@@ -100,7 +102,12 @@ export const useLogin = (onFinishLogin) => {
break;
}
case LoginSteps.passwordAuth: {
await db.user.authenticatePassword(email.current, password.current);
await db.user.authenticatePassword(
email.current,
password.current,
null,
sessionExpired
);
finishLogin();
break;
}

View File

@@ -17,12 +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 React from "react";
import React, { PropsWithChildren } from "react";
import { View } from "react-native";
import { useSelectionStore } from "../../stores/use-selection-store";
import { useThemeColors } from "@notesnook/theme";
export const ContainerHeader = ({ children }) => {
export const ContainerHeader = (props: PropsWithChildren) => {
const { colors } = useThemeColors();
const selectionMode = useSelectionStore((state) => state.selectionMode);
@@ -34,7 +34,7 @@ export const ContainerHeader = ({ children }) => {
overflow: "hidden"
}}
>
{children}
{props.children}
</View>
) : null;
};

View File

@@ -17,8 +17,9 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { useThemeColors } from "@notesnook/theme";
import React, { useCallback, useEffect } from "react";
import { Keyboard, Platform, View } from "react-native";
import { Keyboard, View } from "react-native";
import Animated, {
Easing,
useAnimatedStyle,
@@ -34,14 +35,20 @@ import { getElevationStyle } from "../../utils/elevation";
import { SIZE, normalize } from "../../utils/size";
import NativeTooltip from "../../utils/tooltip";
import { PressableButton } from "../ui/pressable";
import { useThemeColors } from "@notesnook/theme";
export const FloatingButton = ({
interface FloatingButton {
title?: string;
onPress: () => void;
color?: string;
alwaysVisible?: boolean;
}
const FloatingButton = ({
title,
onPress,
color,
shouldShow = false
}) => {
alwaysVisible = false
}: FloatingButton) => {
const { colors } = useThemeColors();
const deviceMode = useSettingStore((state) => state.deviceMode);
const selectionMode = useSelectionStore((state) => state.selectionMode);
@@ -60,12 +67,8 @@ export const FloatingButton = ({
};
});
useEffect(() => {
animate(selectionMode ? 150 : 0);
}, [animate, selectionMode]);
const animate = useCallback(
(toValue) => {
(toValue: number) => {
translate.value = withTiming(toValue, {
duration: 250,
easing: Easing.elastic(1)
@@ -74,39 +77,39 @@ export const FloatingButton = ({
[translate]
);
const onKeyboardHide = useCallback(async () => {
editorState().keyboardState = false;
if (deviceMode !== "mobile") return;
animate(0);
}, [animate, deviceMode]);
const onKeyboardShow = useCallback(async () => {
editorState().keyboardState = true;
if (deviceMode !== "mobile") return;
animate(150);
}, [animate, deviceMode]);
useEffect(() => {
animate(selectionMode ? 150 : 0);
}, [animate, selectionMode]);
useEffect(() => {
let sub1 = Keyboard.addListener("keyboardDidShow", onKeyboardShow);
let sub2 = Keyboard.addListener("keyboardDidHide", onKeyboardHide);
return () => {
sub1?.remove();
sub2?.remove();
const onKeyboardHide = () => {
editorState().keyboardState = false;
if (deviceMode !== "mobile") return;
animate(0);
};
}, [deviceMode, onKeyboardHide, onKeyboardShow]);
const paddings = {
ios: 20,
android: 20,
iPad: 20
};
return deviceMode !== "mobile" && !shouldShow ? null : (
const onKeyboardShow = () => {
editorState().keyboardState = true;
if (deviceMode !== "mobile") return;
animate(150);
};
const sub = [
Keyboard.addListener("keyboardDidShow", onKeyboardShow),
Keyboard.addListener("keyboardDidHide", onKeyboardHide)
];
return () => {
sub.forEach((sub) => sub?.remove?.());
};
}, [deviceMode, animate]);
return deviceMode !== "mobile" && !alwaysVisible ? null : (
<Animated.View
style={[
{
position: "absolute",
right: 12,
bottom: paddings[Platform.isPad ? "iPad" : Platform.OS],
bottom: 20,
zIndex: 10
},
animatedStyle
@@ -115,16 +118,15 @@ export const FloatingButton = ({
<PressableButton
testID={notesnook.buttons.add}
type="accent"
buttonType={{
color: colors.static[color],
text: colors.static.white
}}
accentColor={colors.static[color as keyof typeof colors.static]}
customStyle={{
...getElevationStyle(5),
borderRadius: 100
}}
onLongPress={(event) => {
NativeTooltip.show(event, title, NativeTooltip.POSITIONS.LEFT);
if (title) {
NativeTooltip.show(event, title, NativeTooltip.POSITIONS.LEFT);
}
}}
onPress={onPress}
>
@@ -138,7 +140,7 @@ export const FloatingButton = ({
>
<Icon
name={title === "Clear all trash" ? "delete" : "plus"}
color="white"
color={colors.primary.accentForeground}
size={SIZE.xxl}
/>
</View>
@@ -146,3 +148,5 @@ export const FloatingButton = ({
</Animated.View>
);
};
export { FloatingButton };

View File

@@ -17,44 +17,40 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import React from "react";
import { KeyboardAvoidingView, Platform, View } from "react-native";
import React, { PropsWithChildren } from "react";
import { KeyboardAvoidingView, Platform } from "react-native";
import useGlobalSafeAreaInsets from "../../hooks/use-global-safe-area-insets";
import useIsFloatingKeyboard from "../../hooks/use-is-floating-keyboard";
import { useSettingStore } from "../../stores/use-setting-store";
import { Header } from "../header";
import SelectionHeader from "../selection-header";
export const Container = ({ children }) => {
export const Container = ({ children }: PropsWithChildren) => {
const floating = useIsFloatingKeyboard();
const introCompleted = useSettingStore(
(state) => state.settings.introCompleted
);
const insets = useGlobalSafeAreaInsets();
return (
<KeyboardAvoidingView
behavior="padding"
enabled={Platform.OS === "ios" && !floating}
style={{
flex: 1
flex: 1,
overflow: "hidden",
paddingTop: Platform.OS === "android" ? 0 : insets.top,
paddingBottom: Platform.OS === "android" ? 0 : insets.bottom
}}
>
<View
style={{
flex: 1,
overflow: "hidden",
paddingTop: Platform.OS === "android" ? 0 : insets.top,
paddingBottom: Platform.OS === "android" ? 0 : insets.bottom
}}
>
{!introCompleted ? null : (
<>
<SelectionHeader />
<Header title="Header" screen="Header" />
</>
)}
{!introCompleted ? null : (
<>
<SelectionHeader />
<Header />
</>
)}
{children}
</View>
{children}
</KeyboardAvoidingView>
);
};

View File

@@ -17,7 +17,7 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import React, { useEffect } from "react";
import React, { useEffect, useRef, useState } from "react";
import {
KeyboardAvoidingView,
Modal,
@@ -31,6 +31,9 @@ import useIsFloatingKeyboard from "../../hooks/use-is-floating-keyboard";
import { useSettingStore } from "../../stores/use-setting-store";
import { BouncingView } from "../ui/transitions/bouncing-view";
import { ScopedThemeProvider } from "@notesnook/theme";
import SettingsService from "../../services/settings";
import { useUserStore } from "../../stores/use-user-store";
import { useAppState } from "../../hooks/use-app-state";
const BaseDialog = ({
visible,
@@ -48,9 +51,13 @@ const BaseDialog = ({
bounce = true,
closeOnTouch = true,
useSafeArea = true,
avoidKeyboardResize = false
avoidKeyboardResize = false,
enableSheetKeyboardHandler = false
}) => {
const floating = useIsFloatingKeyboard();
const appState = useAppState();
const lockEvents = useRef(false);
const [internalVisible, setIntervalVisible] = useState(true);
useEffect(() => {
return () => {
@@ -58,14 +65,34 @@ const BaseDialog = ({
};
}, []);
useEffect(() => {
if (useUserStore.getState().disableAppLockRequests) return;
if (SettingsService.get().appLockMode === "background") {
if (appState === "background") {
setIntervalVisible(false);
if (useUserStore.getState().appLocked) {
lockEvents.current = true;
const unsub = useUserStore.subscribe((state) => {
if (!state.appLocked) {
setIntervalVisible(true);
unsub();
setTimeout(() => {
lockEvents.current = false;
});
}
});
}
}
}
}, [appState]);
const Wrapper = useSafeArea ? SafeAreaView : View;
return (
<ScopedThemeProvider value="dialog">
<Modal
visible={visible}
visible={visible && internalVisible}
transparent={true}
animated
statusBarTranslucent={statusBarTranslucent}
supportedOrientations={[
"portrait",
@@ -75,13 +102,17 @@ const BaseDialog = ({
"landscape-right"
]}
onShow={() => {
if (lockEvents.current) return;
if (onShow) {
onShow();
useSettingStore.getState().setSheetKeyboardHandler(false);
if (!enableSheetKeyboardHandler) {
useSettingStore.getState().setSheetKeyboardHandler(false);
}
}
}}
animationType={animation}
onRequestClose={() => {
if (lockEvents.current) return;
if (!closeOnTouch) return null;
useSettingStore.getState().setSheetKeyboardHandler(true);
onRequestClose && onRequestClose();
@@ -107,6 +138,7 @@ const BaseDialog = ({
style={[
styles.backdrop,
{
alignItems: centered ? "center" : undefined,
justifyContent: centered
? "center"
: bottom
@@ -133,8 +165,7 @@ const styles = StyleSheet.create({
backdrop: {
width: "100%",
height: "100%",
justifyContent: "center",
alignItems: "center"
justifyContent: "center"
},
overlayButton: {
width: "100%",

View File

@@ -48,7 +48,7 @@ export const ProFeatures = ({ count = 6 }) => {
content: "Instantly sync to unlimited devices"
},
{
content: "A private vault to keep everything imporant always locked"
content: "A private vault to keep everything important always locked"
},
{
content:

View File

@@ -442,6 +442,7 @@ export class VaultDialog extends Component {
if (this.state.biometricUnlock && !this.state.isBiometryEnrolled) {
await this._enrollFingerprint(this.password);
}
if (this.state.goToEditor) {
this._openInEditor(note);
} else if (this.state.share) {
@@ -452,7 +453,6 @@ export class VaultDialog extends Component {
await this._copyNote(note);
}
} catch (e) {
console.log(e);
this._takeErrorAction(e);
}
};
@@ -467,44 +467,40 @@ export class VaultDialog extends Component {
}
async _enrollFingerprint(password) {
try {
this.setState(
{
loading: true
},
async () => {
try {
await db.vault.unlock(password);
await BiometricService.storeCredentials(password);
this.setState({
loading: false
});
eSendEvent("vaultUpdated");
ToastEvent.show({
heading: "Biometric unlocking enabled!",
message: "Now you can unlock notes in vault with biometrics.",
type: "success",
context: "global"
});
this.close();
} catch (e) {
ToastEvent.show({
heading: "Incorrect password",
message:
"Please enter the correct vault password to enable biometrics.",
type: "error",
context: "local"
});
this.setState({
loading: false
});
return;
}
this.setState(
{
loading: true
},
async () => {
try {
await db.vault.unlock(password);
await BiometricService.storeCredentials(password);
this.setState({
loading: false
});
eSendEvent("vaultUpdated");
ToastEvent.show({
heading: "Biometric unlocking enabled!",
message: "Now you can unlock notes in vault with biometrics.",
type: "success",
context: "global"
});
this.close();
} catch (e) {
this.close();
ToastEvent.show({
heading: "Incorrect password",
message:
"Please enter the correct vault password to enable biometrics.",
type: "error",
context: "local"
});
this.setState({
loading: false
});
}
);
} catch (e) {
this._takeErrorAction(e);
}
}
);
}
async _createVault() {
@@ -543,7 +539,7 @@ export class VaultDialog extends Component {
.remove(this.state.note.id, this.password)
.then(() => {
ToastEvent.show({
heading: "Note permanantly unlocked.",
heading: "Note permanently unlocked.",
type: "success",
context: "global"
});

View File

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

View File

@@ -43,7 +43,7 @@ const _Header = () => {
);
const onScroll = useCallback(
(data) => {
(data: { x: number; y: number }) => {
if (data.y > 150) {
if (!hide) return;
setHide(false);

View File

@@ -43,12 +43,6 @@ export const LeftMenus = () => {
return;
}
Navigation.goBack();
if (
useNavigationStore.getState().currentScreen.name === "Signup" ||
useNavigationStore.getState().currentScreen.name === "Login"
) {
tabBarRef.current.unlock();
}
};
return isTablet && !canGoBack ? null : (

View File

@@ -19,6 +19,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
import React, { useRef } from "react";
import { Platform, StyleSheet, View } from "react-native";
//@ts-ignore
import Menu from "react-native-reanimated-material-menu";
import { notesnook } from "../../../e2e/test.ids";
import Navigation from "../../services/navigation";
@@ -33,12 +34,15 @@ import { IconButton } from "../ui/icon-button";
export const RightMenus = () => {
const { colors } = useThemeColors();
const { contextMenuColors } = useThemeColors("contextMenu");
const { colors: contextMenuColors } = useThemeColors("contextMenu");
const deviceMode = useSettingStore((state) => state.deviceMode);
const rightButtons = useNavigationStore((state) => state.headerRightButtons);
const buttons = useNavigationStore((state) => state.headerRightButtons);
const currentScreen = useNavigationStore((state) => state.currentScreen.name);
const buttonAction = useNavigationStore((state) => state.buttonAction);
const menuRef = useRef();
const menuRef = useRef<{
show: () => void;
hide: () => void;
}>();
return (
<View style={styles.rightBtnContainer}>
@@ -46,9 +50,12 @@ export const RightMenus = () => {
<IconButton
onPress={async () => {
SearchService.prepareSearch();
Navigation.navigate({
name: "Search"
});
Navigation.navigate(
{
name: "Search"
},
{}
);
}}
testID="icon-search"
name="magnify"
@@ -82,7 +89,7 @@ export const RightMenus = () => {
/>
) : null}
{rightButtons && rightButtons.length > 0 ? (
{buttons && buttons.length > 0 ? (
<Menu
ref={menuRef}
animationDuration={200}
@@ -93,7 +100,7 @@ export const RightMenus = () => {
onRequestClose={() => {
menuRef.current?.hide();
}}
button={
anchor={
<IconButton
onPress={() => {
menuRef.current?.show();
@@ -104,7 +111,7 @@ export const RightMenus = () => {
/>
}
>
{rightButtons.map((item) => (
{buttons.map((item) => (
<Button
style={{
width: 150,
@@ -113,7 +120,7 @@ export const RightMenus = () => {
}}
type="gray"
buttonType={{
text: colors.primary.paragraph
text: contextMenuColors.primary.paragraph
}}
key={item.title}
title={item.title}

View File

@@ -17,8 +17,9 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import React, { useEffect, useState } from "react";
import { Platform, View } from "react-native";
import { useThemeColors } from "@notesnook/theme";
import React, { useCallback, useEffect, useState } from "react";
import { Platform } from "react-native";
import { db } from "../../common/database";
import Notebook from "../../screens/notebook";
import {
@@ -26,14 +27,12 @@ import {
eUnSubscribeEvent
} from "../../services/event-manager";
import useNavigationStore from "../../stores/use-navigation-store";
import { useThemeColors } from "@notesnook/theme";
import { eScrollEvent } from "../../utils/events";
import { SIZE } from "../../utils/size";
import Heading from "../ui/typography/heading";
import { useCallback } from "react";
import Tag from "../ui/tag";
import Heading from "../ui/typography/heading";
const titleState = {};
const titleState: { [name: string]: boolean } = {};
export const Title = () => {
const { colors } = useThemeColors();
@@ -42,12 +41,12 @@ export const Title = () => {
const isTopic = currentScreen?.name === "TopicNotes";
const [hide, setHide] = useState(
isNotebook
? typeof titleState[currentScreen.id] === "boolean"
? titleState[currentScreen.id]
? typeof titleState[currentScreen.id as string] === "boolean"
? titleState[currentScreen.id as string]
: true
: false
);
const isHidden = titleState[currentScreen.id];
const isHidden = titleState[currentScreen.id as string];
const notebook =
isTopic && currentScreen.notebookId
? db.notebooks?.notebook(currentScreen.notebookId)?.data
@@ -56,18 +55,18 @@ export const Title = () => {
const isTag = currentScreen?.name === "TaggedNotes";
const onScroll = useCallback(
(data) => {
(data: { x: number; y: number }) => {
if (currentScreen.name !== "Notebook") {
setHide(false);
return;
}
if (data.y > 150) {
if (!hide) return;
titleState[currentScreen.id] = false;
titleState[currentScreen.id as string] = false;
setHide(false);
} else {
if (hide) return;
titleState[currentScreen.id] = true;
titleState[currentScreen.id as string] = true;
setHide(true);
}
},
@@ -76,13 +75,13 @@ export const Title = () => {
useEffect(() => {
if (currentScreen.name === "Notebook") {
let value =
typeof titleState[currentScreen.id] === "boolean"
? titleState[currentScreen.id]
const value =
typeof titleState[currentScreen.id as string] === "boolean"
? titleState[currentScreen.id as string]
: true;
setHide(value);
} else {
setHide(titleState[currentScreen.id]);
setHide(titleState[currentScreen.id as string]);
}
}, [currentScreen.id, currentScreen.name]);
@@ -98,14 +97,7 @@ export const Title = () => {
Notebook.navigate(notebook, true);
}
return (
<View
style={{
opacity: 1,
flexShrink: 1,
flexDirection: "row",
alignItems: "center"
}}
>
<>
{!hide && !isHidden ? (
<Heading
onPress={navigateToNotebook}
@@ -122,10 +114,17 @@ export const Title = () => {
#
</Heading>
) : null}
{title}
{title}{" "}
<Tag
visible={currentScreen.beta}
text="BETA"
style={{
backgroundColor: "transparent"
}}
textColor={colors.primary.accent}
/>
</Heading>
) : null}
<Tag visible={currentScreen.beta} text="BETA" />
</View>
</>
);
};

View File

@@ -18,7 +18,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import Sodium from "@ammarahmed/react-native-sodium";
import dataurl from "@notesnook/core/utils/dataurl";
import dataurl from "@notesnook/core/dist/utils/dataurl";
import { useThemeColors } from "@notesnook/theme";
import React, { useEffect, useState } from "react";
import { Platform, View } from "react-native";

View File

@@ -17,35 +17,25 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import React, { useEffect, useRef, useState } from "react";
import { TouchableOpacity, useWindowDimensions, View } from "react-native";
import { useThemeColors } from "@notesnook/theme";
import { useSettingStore } from "../../../stores/use-setting-store";
import {
eSendEvent,
eSubscribeEvent,
eUnSubscribeEvent,
presentSheet
} from "../../../services/event-manager";
import React, { useRef } from "react";
import { TouchableOpacity, View, useWindowDimensions } from "react-native";
import { eSendEvent, presentSheet } from "../../../services/event-manager";
import SettingsService from "../../../services/settings";
import { GROUP } from "../../../utils/constants";
import { useSettingStore } from "../../../stores/use-setting-store";
import { ColorValues } from "../../../utils/colors";
import { db } from "../../../common/database";
import { GROUP } from "../../../utils/constants";
import { eOpenJumpToDialog } from "../../../utils/events";
import { SIZE } from "../../../utils/size";
import { IconButton } from "../../ui/icon-button";
import { Button } from "../../ui/button";
import Sort from "../../sheets/sort";
import { Button } from "../../ui/button";
import { IconButton } from "../../ui/icon-button";
import Heading from "../../ui/typography/heading";
import { useCallback } from "react";
export const SectionHeader = React.memo(
function SectionHeader({ item, index, type, color, screen }) {
function SectionHeader({ item, index, type, color, screen, groupOptions }) {
const { colors } = useThemeColors();
const { fontScale } = useWindowDimensions();
const [groupOptions, setGroupOptions] = useState(
db.settings?.getGroupOptions(type)
);
let groupBy = Object.keys(GROUP).find(
(key) => GROUP[key] === groupOptions.groupBy
);
@@ -65,17 +55,6 @@ export const SectionHeader = React.memo(
? "Default"
: groupBy.slice(0, 1).toUpperCase() + groupBy.slice(1, groupBy.length);
const onUpdate = useCallback(() => {
setGroupOptions({ ...db.settings?.getGroupOptions(type) });
}, [type]);
useEffect(() => {
eSubscribeEvent("groupOptionsUpdate", onUpdate);
return () => {
eUnSubscribeEvent("groupOptionsUpdate", onUpdate);
};
}, [onUpdate]);
return (
<View
style={{

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 { getUpcomingReminder } from "@notesnook/core/collections/reminders";
import { getUpcomingReminder } from "@notesnook/core/dist/collections/reminders";
import { decode, EntityLevel } from "entities";
import React from "react";
import { View } from "react-native";
@@ -40,6 +40,7 @@ import { TimeSince } from "../../ui/time-since";
import Heading from "../../ui/typography/heading";
import Paragraph from "../../ui/typography/paragraph";
import { useIsCompactModeEnabled } from "../../../hooks/use-is-compact-mode-enabled";
import { useEditorStore } from "../../../stores/use-editor-store";
function navigateToTag(item) {
const tag = db.tags.tag(item.id);
@@ -98,6 +99,9 @@ const NoteItem = ({
dateBy = "dateCreated",
noOpen = false
}) => {
const isEditingNote = useEditorStore(
(state) => state.currentEditingNote === item.id
);
const { colors } = useThemeColors();
const compactMode = useIsCompactModeEnabled(item);
const attachmentCount = db.attachments?.ofNote(item.id, "all")?.length || 0;
@@ -108,6 +112,8 @@ const NoteItem = ({
const reminder = getUpcomingReminder(reminders);
const noteColor = ColorValues[item.color?.toLowerCase()];
const tags = getTags(item);
const primaryColors = isEditingNote ? colors.selected : colors.primary;
return (
<>
<View
@@ -148,7 +154,7 @@ const NoteItem = ({
borderRadius: 5,
marginRight: 5,
borderWidth: 0.5,
borderColor: colors.primary.border,
borderColor: primaryColors.border,
paddingHorizontal: 6,
marginBottom: 5
}}
@@ -179,7 +185,7 @@ const NoteItem = ({
<Paragraph
numberOfLines={1}
color={
ColorValues[item.color?.toLowerCase()] || colors.primary.heading
ColorValues[item.color?.toLowerCase()] || primaryColors.heading
}
style={{
flexWrap: "wrap"
@@ -192,7 +198,7 @@ const NoteItem = ({
<Heading
numberOfLines={1}
color={
ColorValues[item.color?.toLowerCase()] || colors.primary.heading
ColorValues[item.color?.toLowerCase()] || primaryColors.heading
}
style={{
flexWrap: "wrap"
@@ -208,6 +214,7 @@ const NoteItem = ({
style={{
flexWrap: "wrap"
}}
color={primaryColors.paragraph}
numberOfLines={2}
>
{decode(item.headline, {
@@ -262,7 +269,7 @@ const NoteItem = ({
<Icon
name="attachment"
size={SIZE.md}
color={colors.primary.icon}
color={primaryColors.icon}
/>
<Paragraph
color={colors.secondary.paragraph}
@@ -283,7 +290,7 @@ const NoteItem = ({
}}
color={
ColorValues[item.color?.toLowerCase()] ||
colors.primary.accent
primaryColors.accent
}
/>
) : null}
@@ -296,7 +303,7 @@ const NoteItem = ({
style={{
marginRight: 6
}}
color={colors.primary.icon}
color={primaryColors.icon}
/>
) : null}
@@ -354,7 +361,7 @@ const NoteItem = ({
</Paragraph>
<Paragraph
color={colors.primary.accent}
color={primaryColors.accent}
size={SIZE.xs}
style={{
marginRight: 6
@@ -424,7 +431,7 @@ const NoteItem = ({
<IconButton
testID={notesnook.listitem.menu}
color={colors.primary.paragraph}
color={primaryColors.paragraph}
name="dots-horizontal"
size={SIZE.xl}
onPress={() => !noOpen && showActionSheet(item, isTrash)}

View File

@@ -77,7 +77,7 @@ export const openNotebookTopic = (item) => {
useTrashStore.getState().setTrash();
useSelectionStore.getState().setSelectionMode(false);
ToastEvent.show({
heading: "Permanantly deleted items",
heading: "Permanently deleted items",
type: "success",
context: "local"
});

View File

@@ -25,13 +25,13 @@ import { useEditorStore } from "../../../stores/use-editor-store";
export const Filler = ({ item }) => {
const { colors } = useThemeColors();
const currentEditingNote = useEditorStore(
(state) => state.currentEditingNote
const isEditingNote = useEditorStore(
(state) => state.currentEditingNote === item.id
);
const [selected] = useIsSelected(item);
return currentEditingNote === item.id || selected ? (
return isEditingNote || selected ? (
<View
style={{
position: "absolute",
@@ -39,12 +39,11 @@ export const Filler = ({ item }) => {
height: "150%",
backgroundColor: colors.selected.background,
borderLeftWidth: 5,
borderLeftColor:
currentEditingNote === item.id
? item.color
? colors.static[item.color]
: colors.selected.accent
: "transparent"
borderLeftColor: isEditingNote
? item.color
? colors.static[item.color]
: colors.selected.accent
: "transparent"
}}
collapsable={false}
/>

View File

@@ -43,6 +43,9 @@ const SelectionWrapper = ({
const onLongPress = () => {
if (!useSelectionStore.getState().selectionMode) {
useSelectionStore.setState({
selectedItemsList: []
});
useSelectionStore.getState().setSelectionMode(true);
}
useSelectionStore.getState().setSelectedItem(item);

View File

@@ -18,7 +18,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import React from "react";
import { View } from "react-native";
import { Dimensions, View } from "react-native";
import Icon from "react-native-vector-icons/MaterialCommunityIcons";
import { useMessageStore } from "../../stores/use-message-store";
import { useThemeColors } from "@notesnook/theme";
@@ -32,6 +32,7 @@ export const Card = ({ color, warning }) => {
color = color ? color : colors.primary.accent;
const messageBoardState = useMessageStore((state) => state.message);
const announcement = useMessageStore((state) => state.announcement);
const fontScale = Dimensions.get("window").fontScale;
return !messageBoardState.visible || announcement || warning ? null : (
<View
@@ -44,7 +45,6 @@ export const Card = ({ color, warning }) => {
type="gray"
customStyle={{
paddingVertical: 12,
width: "95%",
flexDirection: "row",
alignItems: "center",
justifyContent: "space-between",
@@ -54,17 +54,18 @@ export const Card = ({ color, warning }) => {
<View
style={{
flexDirection: "row",
alignItems: "center"
alignItems: "center",
flexShrink: 1
}}
>
<View
style={{
width: 40,
width: 40 * fontScale,
backgroundColor:
messageBoardState.type === "error"
? hexToRGBA(colors.static.red, 0.15)
: hexToRGBA(color, 0.15),
height: 40,
height: 40 * fontScale,
borderRadius: 100,
alignItems: "center",
justifyContent: "center"
@@ -75,6 +76,7 @@ export const Card = ({ color, warning }) => {
color={
messageBoardState.type === "error" ? colors.error.icon : color
}
allowFontScaling
name={messageBoardState.icon}
/>
</View>
@@ -91,7 +93,7 @@ export const Card = ({ color, warning }) => {
</Paragraph>
<Paragraph
style={{
flexWrap: "wrap",
flexWrap: "nowrap",
flexShrink: 1
}}
color={colors.primary.heading}
@@ -101,22 +103,24 @@ export const Card = ({ color, warning }) => {
</View>
</View>
<View
style={{
width: 40,
height: 40,
justifyContent: "center",
alignItems: "center"
}}
>
<Icon
name="chevron-right"
color={
messageBoardState.type === "error" ? colors.error.icon : color
}
size={SIZE.lg}
/>
</View>
{fontScale > 1 ? null : (
<View
style={{
width: 40,
height: 40,
justifyContent: "center",
alignItems: "center"
}}
>
<Icon
name="chevron-right"
color={
messageBoardState.type === "error" ? colors.error.icon : color
}
size={SIZE.lg}
/>
</View>
)}
</PressableButton>
</View>
);

View File

@@ -39,6 +39,7 @@ import { Empty } from "./empty";
import { getTotalNotes } from "@notesnook/common";
import { useSettingStore } from "../../stores/use-setting-store";
import ReminderItem from "../list-items/reminder";
import { useGroupOptions } from "../../hooks/use-group-options";
const renderItems = {
note: NoteWrapper,
@@ -53,15 +54,10 @@ const renderItems = {
const RenderItem = ({ item, index, type, ...restArgs }) => {
if (!item) return <View />;
const Item = renderItems[item.itemType || item.type] || View;
const groupOptions = db.settings?.getGroupOptions(type);
const dateBy =
groupOptions.sortBy !== "title" ? groupOptions.sortBy : "dateEdited";
const totalNotes = getTotalNotes(item);
return (
<Item
item={item}
dateBy={dateBy}
index={index}
type={type}
totalNotes={totalNotes}
@@ -103,6 +99,13 @@ const List = ({
(type === "notes" && notesListMode === "compact") ||
type === "notebooks" ||
notebooksListMode === "compact";
const groupType =
screen === "Home" ? "home" : screen === "Favorites" ? "favorites" : type;
const groupOptions = useGroupOptions(groupType);
const dateBy =
groupOptions.sortBy !== "title" ? groupOptions.sortBy : "dateEdited";
const renderItem = React.useCallback(
({ item, index }) => (
@@ -111,18 +114,22 @@ const List = ({
index={index}
color={headerProps?.color}
title={headerProps?.heading}
type={
screen === "Notes"
? "home"
: screen === "Favorites"
? "favorites"
: type
}
dateBy={dateBy}
type={groupType}
screen={screen}
isSheet={isSheet}
groupOptions={groupOptions}
/>
),
[headerProps?.color, headerProps?.heading, screen, type, isSheet]
[
headerProps?.color,
headerProps?.heading,
screen,
isSheet,
dateBy,
groupType,
groupOptions
]
);
const _onRefresh = async () => {
@@ -228,7 +235,7 @@ const List = ({
<JumpToSectionDialog
screen={screen}
data={listData}
type={screen === "Notes" ? "home" : type}
type={screen === "Home" ? "home" : type}
scrollRef={scrollRef}
/>
) : null}

View File

@@ -245,13 +245,17 @@ const MergeConflicts = () => {
};
return !visible ? null : (
<Modal
<BaseDialog
statusBarTranslucent
transparent={false}
animationType="slide"
animated={false}
bounce={false}
onRequestClose={() => {
close();
}}
centered={false}
background={colors?.primary.background}
supportedOrientations={[
"portrait",
"portrait-upside-down",
@@ -363,7 +367,7 @@ const MergeConflicts = () => {
</Animated.View>
</View>
</SafeAreaView>
</Modal>
</BaseDialog>
);
};

View File

@@ -84,7 +84,7 @@ export default function NotePreview({ session, content, note }) {
useTrashStore.getState().setTrash();
useSelectionStore.getState().setSelectionMode(false);
ToastEvent.show({
heading: "Permanantly deleted items",
heading: "Permanently deleted items",
type: "success",
context: "local"
});
@@ -109,7 +109,6 @@ export default function NotePreview({ session, content, note }) {
flex: 1
}}
>
<EditorOverlay editorId={editorId} />
<Editor
noHeader
noToolbar
@@ -117,7 +116,6 @@ export default function NotePreview({ session, content, note }) {
editorId={editorId}
onLoad={async () => {
const _note = note || db.notes.note(session?.noteId)?.data;
await sleep(1000);
eSendEvent(eOnLoadNote + editorId, {
..._note,
content: {

View File

@@ -56,7 +56,7 @@ export const CompactFeatures = ({
icon: "shield"
},
{
highlight: "Daily, weekly & montly",
highlight: "Daily, weekly & monthly",
content: "recurring reminders",
icon: "bell"
},

View File

@@ -165,7 +165,12 @@ export const Component = ({ close, promo }) => {
}}
size={SIZE.md}
>
({pricing?.product?.localizedPrice} / mo)
(
{Platform.OS === "android"
? pricing.product?.subscriptionOfferDetails[0].pricingPhases
.pricingPhaseList?.[0].formattedPrice
: pricing.product?.localizedPrice}{" "}
/ mo)
</Paragraph>
)}

View File

@@ -48,23 +48,23 @@ export const Expiring = () => {
const [visible, setVisible] = useState(false);
const [status, setStatus] = useState({
title: "Your trial is ending soon",
offer: null,
offer: "Get 30% off",
extend: true
});
const pricing = usePricing("yearly");
const promo = status.offer
? {
promoCode:
pricing?.info?.discount > 30
? pricing.info.sku
: "com.streetwriters.notesnook.sub.yr.trialoffer",
text: `GET ${
pricing?.info?.discount > 30 ? pricing?.info?.discount : 30
}% OFF on yearly`,
discount: pricing?.info?.discount > 30 ? pricing?.info?.discount : 30
}
: null;
const promo =
status.offer && pricing?.info
? {
promoCode:
pricing?.info?.discount > 30
? pricing.info.sku
: "com.streetwriters.notesnook.sub.yr.trialoffer",
text: `GET ${
pricing?.info?.discount > 30 ? pricing?.info?.discount : 30
}% OFF on yearly`,
discount: pricing?.info?.discount > 30 ? pricing?.info?.discount : 30
}
: null;
useEffect(() => {
eSubscribeEvent(eOpenTrialEndingDialog, open);

View File

@@ -98,7 +98,7 @@ export const features = [
icon: "bell"
},
{
highlight: "Daily, weekly & montly",
highlight: "Daily, weekly & monthly",
content: "reminders",
icon: "refresh",
pro: true
@@ -194,7 +194,7 @@ export const features = [
"Having the right tool at the right time is crucial for note taking. Lists, tables, codeblocks — you name it, we have it.",
features: [
{
highlight: "Basic formating",
highlight: "Basic formatting",
content: "and lists",
icon: "format-bold"
},

View File

@@ -18,13 +18,29 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import React from "react";
import { View } from "react-native";
import { Platform, View } from "react-native";
import { SIZE } from "../../utils/size";
import { PressableButton } from "../ui/pressable";
import Heading from "../ui/typography/heading";
import Paragraph from "../ui/typography/paragraph";
import RNIap from "react-native-iap";
export const PricingItem = ({ product, onPress, compact }) => {
export const PricingItem = ({
product,
onPress,
compact,
strikethrough
}: {
product: {
type: "yearly" | "monthly";
data?: RNIap.Subscription;
info: string;
offerType?: "yearly" | "monthly";
};
strikethrough?: boolean;
onPress?: () => void;
compact?: boolean;
}) => {
return (
<PressableButton
onPress={onPress}
@@ -36,8 +52,10 @@ export const PricingItem = ({ product, onPress, compact }) => {
paddingHorizontal: 12,
paddingVertical: compact ? 15 : 10,
width: compact ? null : "100%",
minWidth: 150
minWidth: 150,
opacity: strikethrough ? 0.7 : 1
}}
disabled={strikethrough}
>
{!compact && (
<View>
@@ -53,8 +71,25 @@ export const PricingItem = ({ product, onPress, compact }) => {
)}
<View>
<Paragraph size={SIZE.sm}>
<Heading size={SIZE.lg - 2}>{product?.data?.localizedPrice}/</Heading>
<Paragraph
style={{
textDecorationLine: strikethrough ? "line-through" : undefined
}}
size={SIZE.sm}
>
<Heading
style={{
textDecorationLine: strikethrough ? "line-through" : undefined
}}
size={SIZE.lg - 2}
>
{Platform.OS === "android"
? (product.data as RNIap.SubscriptionAndroid | undefined)
?.subscriptionOfferDetails[0].pricingPhases
.pricingPhaseList?.[0]?.formattedPrice
: (product.data as RNIap.SubscriptionIOS | undefined)
?.localizedPrice}
</Heading>
{product?.type === "yearly" || product?.offerType === "yearly"
? "/year"
: "/month"}

View File

@@ -20,7 +20,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
import React, { useCallback, useEffect, useState } from "react";
import { ActivityIndicator, Platform, Text, View } from "react-native";
import * as RNIap from "react-native-iap";
import { db } from "../../common/database";
import { DatabaseLogger, db } from "../../common/database";
import { usePricing } from "../../hooks/use-pricing";
import {
eSendEvent,
@@ -47,11 +47,29 @@ import Heading from "../ui/typography/heading";
import Paragraph from "../ui/typography/paragraph";
import { Walkthrough } from "../walkthroughs";
import { PricingItem } from "./pricing-item";
import { useSettingStore } from "../../stores/use-setting-store";
const UUID_PREFIX = "0bdaea";
const UUID_VERSION = "4";
const UUID_VARIANT = "a";
function toUUID(str: string) {
return [
UUID_PREFIX + str.substring(0, 2), // 6 digit prefix + first 2 oid digits
str.substring(2, 6), // # next 4 oid digits
UUID_VERSION + str.substring(6, 9), // # 1 digit version(0x4) + next 3 oid digits
UUID_VARIANT + str.substring(9, 12), // # 1 digit variant(0b101) + 1 zero bit + next 3 oid digits
str.substring(12)
].join("-");
}
const promoCyclesMonthly = {
1: "first month",
2: "first 2 months",
3: "first 3 months"
3: "first 3 months",
4: "first 4 months",
5: "first 5 months",
6: "first 3 months"
};
const promoCyclesYearly = {
@@ -65,10 +83,24 @@ export const PricingPlans = ({
marginTop,
heading = true,
compact = false
}: {
promo?: {
promoCode: string;
};
marginTop?: any;
heading?: boolean;
compact?: boolean;
}) => {
const { colors } = useThemeColors();
const user = useUserStore((state) => state.user);
const [product, setProduct] = useState(null);
const [product, setProduct] = useState<{
type: string;
offerType: "monthly" | "yearly";
data: RNIap.Subscription;
cycleText: string;
info: string;
}>();
const [buying, setBuying] = useState(false);
const [loading, setLoading] = useState(false);
const userCanRequestTrial =
@@ -90,27 +122,40 @@ export const PricingPlans = ({
}
}, [promo?.promoCode]);
const getPromo = async (code) => {
const getPromo = async (code: string) => {
try {
let productId;
let skuId: string;
if (code.startsWith("com.streetwriters.notesnook")) {
productId = code;
skuId = code;
} else {
productId = await db.offers.getCode(code.split(":")[0], Platform.OS);
skuId = await db.offers?.getCode(code.split(":")[0], Platform.OS);
}
let products = await PremiumService.getProducts();
let product = products.find((p) => p.productId === productId);
const products = await PremiumService.getProducts();
const product = products.find((p) => p.productId === skuId);
if (!product) return false;
let isMonthly = product.productId.indexOf(".mo") > -1;
let cycleText = isMonthly
const isMonthly = product.productId.indexOf(".mo") > -1;
const cycleText = isMonthly
? promoCyclesMonthly[
product.introductoryPriceCyclesAndroid ||
product.introductoryPriceNumberOfPeriodsIOS
(Platform.OS === "android"
? (product as RNIap.SubscriptionAndroid)
.subscriptionOfferDetails[0]?.pricingPhases
.pricingPhaseList?.[0].billingCycleCount
: parseInt(
(product as RNIap.SubscriptionIOS)
.introductoryPriceNumberOfPeriodsIOS as string
)) as keyof typeof promoCyclesMonthly
]
: promoCyclesYearly[
product.introductoryPriceCyclesAndroid ||
product.introductoryPriceNumberOfPeriodsIOS
(Platform.OS === "android"
? (product as RNIap.SubscriptionAndroid)
.subscriptionOfferDetails[0]?.pricingPhases
.pricingPhaseList?.[0].billingCycleCount
: parseInt(
(product as RNIap.SubscriptionIOS)
.introductoryPriceNumberOfPeriodsIOS as string
)) as keyof typeof promoCyclesYearly
];
setProduct({
@@ -118,7 +163,7 @@ export const PricingPlans = ({
offerType: isMonthly ? "monthly" : "yearly",
data: product,
cycleText: cycleText,
info: "Pay monthly, cancel anytime"
info: `Pay ${isMonthly ? "monthly" : "yearly"}, cancel anytime`
});
return true;
} catch (e) {
@@ -131,22 +176,41 @@ export const PricingPlans = ({
getSkus();
}, [getSkus]);
const buySubscription = async (product) => {
if (buying) return;
const buySubscription = async (product: RNIap.Subscription) => {
if (buying || !product) return;
setBuying(true);
try {
if (!user) {
setBuying(false);
return;
}
await RNIap.requestSubscription(
product?.productId,
false,
null,
-1,
user.id,
user.id
useSettingStore.getState().setAppDidEnterBackgroundForAction(true);
const androidOfferToken =
Platform.OS === "android"
? (product as RNIap.SubscriptionAndroid).subscriptionOfferDetails[0]
.offerToken
: null;
DatabaseLogger.info(
`Subscription Requested initiated for user ${toUUID(user.id)}`
);
await RNIap.requestSubscription({
sku: product?.productId,
obfuscatedAccountIdAndroid: user.id,
obfuscatedProfileIdAndroid: user.id,
appAccountToken: toUUID(user.id),
andDangerouslyFinishTransactionAutomaticallyIOS: false,
subscriptionOffers: androidOfferToken
? [
{
offerToken: androidOfferToken,
sku: product?.productId
}
]
: undefined
});
useSettingStore.getState().setAppDidEnterBackgroundForAction(false);
setBuying(false);
eSendEvent(eCloseSheet);
eSendEvent(eClosePremiumDialog);
@@ -167,6 +231,35 @@ export const PricingPlans = ({
}
};
function getStandardPrice() {
if (!product) return;
const productType = product.offerType;
if (Platform.OS === "android") {
const pricingPhaseListItem = (product.data as RNIap.SubscriptionAndroid)
?.subscriptionOfferDetails[0]?.pricingPhases.pricingPhaseList?.[1];
if (!pricingPhaseListItem) {
const product =
productType === "monthly"
? monthlyPlan?.product
: yearlyPlan?.product;
return (product as RNIap.SubscriptionAndroid)
?.subscriptionOfferDetails[0]?.pricingPhases.pricingPhaseList?.[0]
?.formattedPrice;
}
return pricingPhaseListItem?.formattedPrice;
} else {
const productDefault =
productType === "monthly" ? monthlyPlan?.product : yearlyPlan?.product;
return (
(product.data as RNIap.SubscriptionIOS)?.localizedPrice ||
(productDefault as RNIap.SubscriptionIOS)?.localizedPrice
);
}
}
return loading ? (
<View
style={{
@@ -198,7 +291,14 @@ export const PricingPlans = ({
}}
size={SIZE.lg}
>
{PremiumService.getMontlySub().localizedPrice} / mo
{(Platform.OS === "android"
? (monthlyPlan?.product as RNIap.SubscriptionAndroid | undefined)
?.subscriptionOfferDetails[0]?.pricingPhases
.pricingPhaseList?.[0]?.formattedPrice
: (monthlyPlan?.product as RNIap.SubscriptionIOS | undefined)
?.localizedPrice) ||
(PremiumService.getMontlySub() as any)?.localizedPrice}
/ mo
</Paragraph>
<Button
onPress={() => {
@@ -218,7 +318,7 @@ export const PricingPlans = ({
<Button
onPress={async () => {
try {
await db.user.activateTrial();
await db.user?.activateTrial();
eSendEvent(eClosePremiumDialog);
eSendEvent(eCloseSheet);
await sleep(300);
@@ -239,33 +339,91 @@ export const PricingPlans = ({
) : (
<>
{product?.type === "promo" ? (
<Heading
<View
style={{
paddingVertical: 15,
alignSelf: "center",
textAlign: "center"
alignItems: "center"
}}
size={SIZE.lg - 4}
>
{product.data.introductoryPrice}
<Paragraph
{product?.offerType === "monthly" ? (
<PricingItem
product={{
type: "monthly",
data: monthlyPlan?.product,
info: "Pay once a month, cancel anytime."
}}
strikethrough={true}
/>
) : (
<PricingItem
onPress={() => {
if (!monthlyPlan?.product) return;
buySubscription(monthlyPlan?.product);
}}
product={{
type: "yearly",
data: yearlyPlan?.product,
info: "Pay once a year, cancel anytime."
}}
strikethrough={true}
/>
)}
<Heading
style={{
textDecorationLine: "line-through",
color: colors.secondary.paragraph
paddingTop: 15,
fontSize: SIZE.lg
}}
size={SIZE.sm}
>
({product.data.localizedPrice})
</Paragraph>{" "}
for {product.cycleText}
</Heading>
Special offer for you
</Heading>
<View
style={{
paddingVertical: 20,
paddingBottom: 10
}}
>
<Heading
style={{
alignSelf: "center",
textAlign: "center"
}}
size={SIZE.xxl}
>
{Platform.OS === "android"
? (product.data as RNIap.SubscriptionAndroid)
?.subscriptionOfferDetails[0].pricingPhases
.pricingPhaseList?.[0]?.formattedPrice
: (product.data as RNIap.SubscriptionIOS)
?.introductoryPrice ||
(product.data as RNIap.SubscriptionIOS)
?.localizedPrice}{" "}
{product?.cycleText
? `for ${product.cycleText}`
: product?.offerType}
</Heading>
{product?.cycleText ? (
<Paragraph
style={{
color: colors.secondary.paragraph,
alignSelf: "center",
textAlign: "center"
}}
size={SIZE.md}
>
then {getStandardPrice()} {product?.offerType}.
</Paragraph>
) : null}
</View>
</View>
) : null}
{user && !product ? (
<>
{heading || monthlyPlan?.info?.discount > 0 ? (
{heading || (monthlyPlan?.info?.discount || 0) > 0 ? (
<>
{monthlyPlan && monthlyPlan?.info?.discount > 0 ? (
{monthlyPlan && (monthlyPlan?.info?.discount || 0) > 0 ? (
<View
style={{
alignSelf: "center",
@@ -273,7 +431,12 @@ export const PricingPlans = ({
marginBottom: 20
}}
>
<Heading color={colors.primary.accent}>
<Heading
style={{
textAlign: "center"
}}
color={colors.primary.accent}
>
Get {monthlyPlan?.info?.discount}% off in{" "}
{monthlyPlan?.info?.country}
</Heading>
@@ -300,12 +463,15 @@ export const PricingPlans = ({
}}
>
<PricingItem
onPress={() => buySubscription(monthlyPlan?.product)}
onPress={() => {
if (!monthlyPlan?.product) return;
buySubscription(monthlyPlan?.product);
}}
compact={compact}
product={{
type: "monthly",
data: monthlyPlan?.product,
info: "Pay monthly, cancel anytime."
info: "Pay once a month, cancel anytime."
}}
/>
@@ -319,12 +485,15 @@ export const PricingPlans = ({
)}
<PricingItem
onPress={() => buySubscription(yearlyPlan?.product)}
onPress={() => {
if (!yearlyPlan?.product) return;
buySubscription(yearlyPlan?.product);
}}
compact={compact}
product={{
type: "yearly",
data: yearlyPlan?.product,
info: "Pay yearly"
info: "Pay once a year, cancel anytime."
}}
/>
</View>
@@ -346,7 +515,7 @@ export const PricingPlans = ({
eSendEvent(eCloseSimpleDialog);
setBuying(true);
try {
if (!(await getPromo(value)))
if (!(await getPromo(value as string)))
throw new Error("Error applying promo code");
ToastEvent.show({
heading: "Discount applied!",
@@ -358,7 +527,7 @@ export const PricingPlans = ({
setBuying(false);
ToastEvent.show({
heading: "Promo code invalid or expired",
message: e.message,
message: (e as Error).message,
type: "error",
context: "local"
});
@@ -396,7 +565,7 @@ export const PricingPlans = ({
width={250}
style={{
paddingHorizontal: 12,
marginTop: 30,
marginTop: product?.type === "promo" ? 0 : 30,
marginBottom: 10
}}
/>
@@ -427,7 +596,10 @@ export const PricingPlans = ({
) : (
<>
<Button
onPress={() => buySubscription(product.data)}
onPress={() => {
if (!product?.data) return;
buySubscription(product.data);
}}
height={40}
width="50%"
type="accent"
@@ -436,7 +608,7 @@ export const PricingPlans = ({
<Button
onPress={() => {
setProduct(null);
setProduct(undefined);
}}
style={{
marginTop: 5
@@ -500,7 +672,7 @@ export const PricingPlans = ({
textAlign: "center"
}}
>
By subscribing, your will be charged on your Google Account, and
By subscribing, you will be charged on your Google Account, and
your subscription will automatically renew until you cancel prior
to the end of the then current period.
</Paragraph>
@@ -523,7 +695,7 @@ export const PricingPlans = ({
<Paragraph
size={SIZE.xs}
onPress={() => {
openLinkInBrowser("https://notesnook.com/tos", colors)
openLinkInBrowser("https://notesnook.com/tos")
.catch(() => {})
.then(() => {});
}}
@@ -538,7 +710,7 @@ export const PricingPlans = ({
<Paragraph
size={SIZE.xs}
onPress={() => {
openLinkInBrowser("https://notesnook.com/privacy", colors)
openLinkInBrowser("https://notesnook.com/privacy")
.catch(() => {})
.then(() => {});
}}

View File

@@ -16,8 +16,8 @@ GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import React from "react";
import { Platform, View } from "react-native";
import React, { useRef } from "react";
import { Dimensions, Platform, View, useWindowDimensions } from "react-native";
import { FlatList } from "react-native-actions-sheet";
import { db } from "../../common/database";
import { DDS } from "../../services/device-detection";
@@ -54,7 +54,6 @@ export const Properties = ({ close = () => {}, item, buttons = [] }) => {
const { colors } = useThemeColors();
const alias = item.alias || item.title;
const isColor = !!ColorValues[item.title];
if (!item || !item.id) {
return (
<Paragraph style={{ marginVertical: 10, alignSelf: "center" }}>
@@ -62,6 +61,7 @@ export const Properties = ({ close = () => {}, item, buttons = [] }) => {
</Paragraph>
);
}
return (
<FlatList
keyboardShouldPersistTaps="always"
@@ -135,6 +135,7 @@ export const Properties = ({ close = () => {}, item, buttons = [] }) => {
}, 1000);
}}
/>
<Synced item={item} close={close} />
{DDS.isTab ? (

View File

@@ -18,7 +18,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import React from "react";
import { FlatList, ScrollView, View } from "react-native";
import { Dimensions, FlatList, ScrollView, View } from "react-native";
import Icon from "react-native-vector-icons/MaterialCommunityIcons";
import { useActions } from "../../hooks/use-actions";
import { DDS } from "../../services/device-detection";
@@ -33,9 +33,11 @@ export const Items = ({ item, buttons, close }) => {
const dimensions = useSettingStore((state) => state.dimensions);
const actions = useActions({ item, close });
const data = actions.filter((i) => buttons.indexOf(i.id) > -1 && !i.hidden);
let width = dimensions.width > 600 ? 600 : dimensions.width;
let columnItemsCount = DDS.isLargeTablet() ? 7 : 5;
const shouldShrink =
Dimensions.get("window").fontScale > 1 &&
Dimensions.get("window").width < 450;
let columnItemsCount = DDS.isLargeTablet() ? 7 : shouldShrink ? 4 : 5;
let columnItemWidth = DDS.isTab
? (width - 12) / columnItemsCount
: (width - 12) / columnItemsCount;
@@ -66,6 +68,7 @@ export const Items = ({ item, buttons, close }) => {
}}
>
<Icon
allowFontScaling
name={item.icon}
size={DDS.isTab ? SIZE.xxl : SIZE.lg}
color={
@@ -78,7 +81,11 @@ export const Items = ({ item, buttons, close }) => {
/>
</PressableButton>
<Paragraph size={SIZE.xs} style={{ textAlign: "center" }}>
<Paragraph
size={SIZE.xs}
textBreakStrategy="simple"
style={{ textAlign: "center" }}
>
{item.title}
</Paragraph>
</View>
@@ -139,6 +146,7 @@ export const Items = ({ item, buttons, close }) => {
>
<Icon
name={item.icon}
allowFontScaling
size={DDS.isTab ? SIZE.xxl : SIZE.md + 4}
color={
item.on
@@ -150,7 +158,11 @@ export const Items = ({ item, buttons, close }) => {
/>
</PressableButton>
<Paragraph size={SIZE.xxs + 1} style={{ textAlign: "center" }}>
<Paragraph
textBreakStrategy="simple"
size={SIZE.xxs + 1}
style={{ textAlign: "center" }}
>
{item.title}
</Paragraph>
</PressableButton>
@@ -163,9 +175,12 @@ export const Items = ({ item, buttons, close }) => {
"copy",
"share",
"export",
"lock-unlock",
"publish"
"lock-unlock"
];
if (!shouldShrink) {
topBarItemsList.push("publish");
}
const topBarItems = data.filter(
(item) => topBarItemsList.indexOf(item.id) > -1
);

View File

@@ -17,10 +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 React from "react";
import { View } from "react-native";
import Icon from "react-native-vector-icons/MaterialCommunityIcons";
import { useThemeColors } from "@notesnook/theme";
import React from "react";
import { View, useWindowDimensions } from "react-native";
import Icon from "react-native-vector-icons/MaterialCommunityIcons";
import { useUserStore } from "../../stores/use-user-store";
import { openLinkInBrowser } from "../../utils/functions";
import { SIZE } from "../../utils/size";
@@ -33,6 +33,8 @@ export const Synced = ({ item, close }) => {
const user = useUserStore((state) => state.user);
const lastSynced = useUserStore((state) => state.lastSynced);
const dimensions = useWindowDimensions();
const shouldShrink = dimensions.fontScale > 1 && dimensions.width < 450;
return user && lastSynced >= item.dateModified ? (
<View
style={{
@@ -49,7 +51,11 @@ export const Synced = ({ item, close }) => {
borderTopColor: colors.secondary.background
}}
>
<Icon name="shield-key-outline" color={colors.primary.accent} size={SIZE.xxxl} />
<Icon
name="shield-key-outline"
color={colors.primary.accent}
size={shouldShrink ? SIZE.xxl : SIZE.xxxl}
/>
<View
style={{
@@ -67,15 +73,17 @@ export const Synced = ({ item, close }) => {
>
Encrypted and synced
</Heading>
<Paragraph
style={{
flexWrap: "wrap"
}}
size={SIZE.xs}
color={colors.primary.paragraph}
>
No one can view this {item.itemType || item.type} except you.
</Paragraph>
{shouldShrink ? null : (
<Paragraph
style={{
flexWrap: "wrap"
}}
size={SIZE.xs}
color={colors.primary.paragraph}
>
No one can view this {item.itemType || item.type} except you.
</Paragraph>
)}
</View>
<Button
@@ -91,8 +99,8 @@ export const Synced = ({ item, close }) => {
console.error(e);
}
}}
fontSize={SIZE.xs}
title="Learn more"
fontSize={SIZE.xs}
height={30}
type="grayAccent"
/>

View File

@@ -207,7 +207,6 @@ export const SelectionHeader = React.memo(() => {
{screen === "Trash" ||
screen === "Notebooks" ||
screen === "Notebook" ||
screen === "Reminders" ? null : (
<>
<IconButton
@@ -224,6 +223,21 @@ export const SelectionHeader = React.memo(() => {
name="pound"
size={SIZE.xl}
/>
<IconButton
onPress={async () => {
ExportNotesSheet.present(selectedItemsList);
}}
tooltipText="Export"
tooltipPosition={4}
customStyle={{
marginLeft: 10
}}
color={colors.primary.paragraph}
name="export"
size={SIZE.xl}
/>
<IconButton
onPress={async () => {
//setSelectionMode(false);
@@ -239,20 +253,6 @@ export const SelectionHeader = React.memo(() => {
name="plus"
size={SIZE.xl}
/>
<IconButton
onPress={async () => {
ExportNotesSheet.present(selectedItemsList);
}}
tooltipText="Export"
tooltipPosition={4}
customStyle={{
marginLeft: 10
}}
color={colors.primary.paragraph}
name="export"
size={SIZE.xl}
/>
</>
)}
@@ -317,23 +317,8 @@ export const SelectionHeader = React.memo(() => {
customStyle={{
marginLeft: 10
}}
onPress={async () => {
presentDialog({
title: `Delete ${
selectedItemsList.length > 1 ? "items" : "item"
}`,
paragraph: `Are you sure you want to delete ${
selectedItemsList.length > 1 ? "these items?" : "this item?"
}`,
positiveText: "Delete",
negativeText: "Cancel",
positivePress: () => {
deleteItems();
},
positiveType: "errorShade"
});
return;
onPress={() => {
deleteItems();
}}
tooltipText="Move to trash"
tooltipPosition={1}

View File

@@ -101,8 +101,13 @@ const SheetProvider = ({ context = "global" }) => {
setVisible(false);
setData(null);
}}
keyboardHandlerDisabled={data?.keyboardHandlerDisabled}
bottomPadding={!data.noBottomPadding}
enableGesturesInScrollView={data.enableGesturesInScrollView}
enableGesturesInScrollView={
typeof data.enableGesturesInScrollView === "undefined"
? true
: data.enableGesturesInScrollView
}
>
<View
style={{
@@ -127,7 +132,7 @@ const SheetProvider = ({ context = "global" }) => {
{data?.icon ? (
<Icon
color={colors[dialogData.iconColor] || colors.primary.accent}
color={colors[data.iconColor] || colors.primary.accent}
name={data.icon}
size={50}
/>
@@ -194,7 +199,6 @@ const SheetProvider = ({ context = "global" }) => {
key={data.actionText}
title={data.actionText}
accentColor={data.iconColor}
accentText="light"
type="accent"
height={40}
width={250}

View File

@@ -18,7 +18,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import React, { useCallback, useEffect, useRef, useState } from "react";
import { FlatList } from "react-native-actions-sheet";
import { FlashList } from "react-native-actions-sheet";
import { db } from "../../../common/database";
import { ListHeaderInputItem } from "./list-header-item.js";
@@ -54,7 +54,7 @@ export const FilteredList = ({
}, [data, onChangeText]);
return (
<FlatList
<FlashList
{...restProps}
data={filtered}
ref={listRef}

View File

@@ -161,9 +161,6 @@ const MoveNoteSheet = ({ note, actionSheetRef }) => {
: "deselected";
if (itemState[notebook.id] === "selected") {
count++;
contextValue.select(notebook);
} else {
contextValue.deselect(notebook);
}
for (let topic of notebook.topics) {
itemState[topic.id] = state
@@ -176,9 +173,6 @@ const MoveNoteSheet = ({ note, actionSheetRef }) => {
: "deselected";
if (itemState[topic.id] === "selected") {
count++;
contextValue.select(topic);
} else {
contextValue.deselect(topic);
}
}
}
@@ -189,7 +183,7 @@ const MoveNoteSheet = ({ note, actionSheetRef }) => {
}
useItemSelectionStore.getState().setItemState(itemState);
},
[contextValue, getSelectedNotesCountInItem, selectedItemsList]
[getSelectedNotesCountInItem, selectedItemsList]
);
const getItemsForItem = (item) => {
@@ -214,7 +208,7 @@ const MoveNoteSheet = ({ note, actionSheetRef }) => {
}
const updateItemState = useCallback(function (item, state) {
const itemState = useItemSelectionStore.getState().itemState;
const itemState = { ...useItemSelectionStore.getState().itemState };
const mergeState = {
[item.id]: state
};
@@ -370,56 +364,93 @@ const MoveNoteSheet = ({ note, actionSheetRef }) => {
</View>
<SelectionProvider value={contextValue}>
<FilteredList
<View
style={{
paddingHorizontal: 12,
maxHeight: dimensions.height * 0.85
maxHeight: dimensions.height * 0.85,
height: 50 * (notebooks.length + 2)
}}
ListEmptyComponent={
notebooks.length > 0 ? null : (
<View
style={{
width: "100%",
height: "100%",
justifyContent: "center",
alignItems: "center"
>
<FilteredList
ListEmptyComponent={
notebooks.length > 0 ? null : (
<View
style={{
width: "100%",
height: "100%",
justifyContent: "center",
alignItems: "center"
}}
>
<Icon
name="book-outline"
color={colors.primary.icon}
size={100}
/>
<Paragraph style={{ marginBottom: 10 }}>
You do not have any notebooks.
</Paragraph>
</View>
)
}
estimatedItemSize={50}
data={notebooks}
hasHeaderSearch={true}
renderItem={({ item, index }) => (
<ListItem
item={item}
key={item.id}
index={index}
hasNotes={getSelectedNotesCountInItem(item) > 0}
sheetRef={actionSheetRef}
infoText={
<>
{item.topics.length === 1
? item.topics.length + " topic"
: item.topics.length + " topics"}
</>
}
getListItems={getItemsForItem}
getSublistItemProps={(topic) => ({
hasNotes: getSelectedNotesCountInItem(topic) > 0,
style: {
marginBottom: 0,
height: 40
},
onPress: (item) => {
const itemState =
useItemSelectionStore.getState().itemState;
const currentState = itemState[item.id];
if (currentState !== "selected") {
resetItemState("deselected");
contextValue.select(item);
} else {
contextValue.deselect(item);
}
},
key: item.id,
type: "transparent"
})}
icon={(expanded) => ({
name: expanded ? "chevron-up" : "chevron-down",
color: expanded
? colors.primary.accent
: colors.primary.paragraph
})}
onScrollEnd={() => {
actionSheetRef.current?.handleChildScrollEnd();
}}
>
<Icon
name="book-outline"
color={colors.primary.icon}
size={100}
/>
<Paragraph style={{ marginBottom: 10 }}>
You do not have any notebooks.
</Paragraph>
</View>
)
}
data={notebooks}
hasHeaderSearch={true}
renderItem={({ item, index }) => (
<ListItem
item={item}
key={item.id}
index={index}
hasNotes={getSelectedNotesCountInItem(item) > 0}
sheetRef={actionSheetRef}
infoText={
<>
{item.topics.length === 1
? item.topics.length + " topic"
: item.topics.length + " topics"}
</>
}
getListItems={getItemsForItem}
getSublistItemProps={(topic) => ({
hasNotes: getSelectedNotesCountInItem(topic) > 0,
style: {
marginBottom: 0,
height: 40
},
onPress: (item) => {
hasSubList={true}
hasHeaderSearch={false}
type="grayBg"
sublistItemType="topic"
onAddItem={(title) => {
return onAddTopic(title, item);
}}
onAddSublistItem={(item) => {
openAddTopicDialog(item);
}}
onPress={(item) => {
const itemState =
useItemSelectionStore.getState().itemState;
const currentState = itemState[item.id];
@@ -429,47 +460,16 @@ const MoveNoteSheet = ({ note, actionSheetRef }) => {
} else {
contextValue.deselect(item);
}
},
key: item.id,
type: "transparent"
})}
icon={(expanded) => ({
name: expanded ? "chevron-up" : "chevron-down",
color: expanded
? colors.primary.accent
: colors.primary.paragraph
})}
onScrollEnd={() => {
actionSheetRef.current?.handleChildScrollEnd();
}}
hasSubList={true}
hasHeaderSearch={false}
type="grayBg"
sublistItemType="topic"
onAddItem={(title) => {
return onAddTopic(title, item);
}}
onAddSublistItem={(item) => {
openAddTopicDialog(item);
}}
onPress={(item) => {
const itemState = useItemSelectionStore.getState().itemState;
const currentState = itemState[item.id];
if (currentState !== "selected") {
resetItemState("deselected");
contextValue.select(item);
} else {
contextValue.deselect(item);
}
}}
/>
)}
itemType="notebook"
onAddItem={async (title) => {
return await onAddNotebook(title);
}}
ListFooterComponent={<View style={{ height: 20 }} />}
/>
}}
/>
)}
itemType="notebook"
onAddItem={async (title) => {
return await onAddNotebook(title);
}}
ListFooterComponent={<View style={{ height: 20 }} />}
/>
</View>
</SelectionProvider>
</View>
</>

View File

@@ -273,6 +273,7 @@ export const ListItem = ({
alignSelf: "flex-end",
maxHeight: 250
}}
estimatedItemSize={40}
itemType={sublistItemType}
hasHeaderSearch={hasHeaderSearch}
renderItem={({ item, index }) => (

View File

@@ -43,6 +43,7 @@ import Paragraph from "../../ui/typography/paragraph";
import { eSendEvent } from "../../../services/event-manager";
import { eCloseSheet } from "../../../utils/events";
import { requestInAppReview } from "../../../services/app-review";
import { Dialog } from "../../dialog";
const ExportNotesSheet = ({ notes, update }) => {
const { colors } = useThemeColors();
@@ -97,6 +98,16 @@ const ExportNotesSheet = ({ notes, update }) => {
id: notesnook.ids.dialogs.export.md,
pro: premium
},
{
title: "Markdown + Frontmatter",
func: async () => {
await save("md-frontmatter");
},
icon: "language-markdown",
desc: "View in any text or markdown editor",
id: notesnook.ids.dialogs.export.md,
pro: premium
},
{
title: "Plain Text",
func: async () => {
@@ -147,6 +158,8 @@ const ExportNotesSheet = ({ notes, update }) => {
</>
) : null}
<Dialog context="export-notes" />
<View style={styles.buttonContainer}>
{!exporting && !complete ? (
actions.map((item) => (
@@ -178,7 +191,9 @@ const ExportNotesSheet = ({ notes, update }) => {
>
<Icon
name={item.icon}
color={item.pro ? colors.primary.accent : colors.primary.icon}
color={
item.pro ? colors.primary.accent : colors.primary.icon
}
size={SIZE.xxxl + 10}
/>
</View>
@@ -334,7 +349,8 @@ ExportNotesSheet.present = (notes, allNotes) => {
notes={allNotes ? db.notes.all : notes}
update={update}
/>
)
),
keyboardHandlerDisabled: true
});
};

View File

@@ -35,10 +35,14 @@ import { presentDialog } from "../../dialog/functions";
import { Button } from "../../ui/button";
import Seperator from "../../ui/seperator";
import Paragraph from "../../ui/typography/paragraph";
import { useStoredRef } from "../../../hooks/use-stored-ref";
export const Issue = ({ defaultTitle, defaultBody, issueTitle }) => {
const { colors } = useThemeColors();
const body = useRef(defaultBody);
const title = useRef(defaultTitle);
const body = useStoredRef("issueBody", defaultBody);
const title = useStoredRef("issueTitle", defaultTitle);
const user = useUserStore((state) => state.user);
const [loading, setLoading] = useState(false);
const bodyRef = useRef();
@@ -70,6 +74,8 @@ Logged in: ${user ? "yes" : "no"}`,
});
setLoading(false);
eSendEvent(eCloseSheet);
body.reset();
title.reset();
await sleep(300);
presentDialog({
title: "Issue reported",
@@ -105,7 +111,7 @@ Logged in: ${user ? "yes" : "no"}`,
} catch (e) {
setLoading(false);
ToastEvent.show({
heading: "An error occured",
heading: "An error occurred",
message: e.message,
type: "error"
});

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 { EVENTS } from "@notesnook/core/common";
import { EVENTS } from "@notesnook/core/dist/common";
import React, { useCallback, useEffect, useState } from "react";
import { Platform, View } from "react-native";
import { db } from "../../../common/database";
@@ -177,7 +177,7 @@ export default function Migrate() {
textAlign: "center"
}}
>
An error occured while migrating your data. You can logout of your
An error occurred while migrating your data. You can logout of your
account and try to relogin. However this is not recommended as it
may result in some data loss if your data was not synced.
</Paragraph>

View File

@@ -64,7 +64,7 @@ export const Progress = () => {
width={null}
animated={true}
useNativeDriver
progress={currentProgress || 0.1}
indeterminate
unfilledColor={colors.secondary.background}
color={colors.primary.accent}
borderWidth={0}
@@ -74,7 +74,7 @@ export const Progress = () => {
{progress ? (
<Paragraph color={colors.secondary.paragraph}>
{progress.type?.slice(0, 1).toUpperCase() + progress.type?.slice(1)}
ing {progress?.current}/{progress?.total}
ing {progress?.current}
</Paragraph>
) : null}
</View>

View File

@@ -37,17 +37,18 @@ import Heading from "../../ui/typography/heading";
import Paragraph from "../../ui/typography/paragraph";
import { requestInAppReview } from "../../../services/app-review";
const PublishNoteSheet = ({ note: item, update }) => {
const PublishNoteSheet = ({ note: item }) => {
const { colors } = useThemeColors();
const actionSheetRef = useRef();
const loading = useAttachmentStore((state) => state.loading);
const attachmentDownloads = useAttachmentStore((state) => state.downloading);
const downloading = attachmentDownloads[`monograph-${item.id}`];
const [selfDestruct, setSelfDestruct] = useState(false);
const [isLocked, setIsLocked] = useState(false);
const [note, setNote] = useState(item);
const [publishing, setPublishing] = useState(false);
const publishUrl =
note &&
`https://monograph.notesnook.com/${db?.monographs.monograph(note?.id)}`;
note && `https://monogr.ph/${db?.monographs.monograph(note?.id)}`;
const isPublished = note && db?.monographs.isPublished(note?.id);
const pwdInput = useRef();
const passwordValue = useRef();
@@ -137,9 +138,9 @@ const PublishNoteSheet = ({ note: item, update }) => {
}}
>
Please wait...
{loading && loading.current && loading.total
{downloading && downloading.current && downloading.total
? `\nDownloading attachments (${
loading?.current / loading?.total
downloading?.current / downloading?.total
})`
: ""}
</Paragraph>

View File

@@ -16,6 +16,7 @@ GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { useThemeColors } from "@notesnook/theme";
import React, { RefObject, useRef, useState } from "react";
import { Platform, TextInput, View } from "react-native";
import { ActionSheetRef, ScrollView } from "react-native-actions-sheet";
@@ -25,7 +26,6 @@ import {
ToastEvent,
presentSheet
} from "../../../services/event-manager";
import { useThemeColors } from "@notesnook/theme";
import { SIZE } from "../../../utils/size";
import { Button } from "../../ui/button";
import Input from "../../ui/input";
@@ -223,7 +223,7 @@ export default function ReminderSheet({
<View
style={{
paddingHorizontal: 12,
maxHeight: DDS.isTab ? "90%" : "99.99%"
maxHeight: DDS.isTab ? "99.99%" : "99.99%"
}}
>
<View
@@ -247,7 +247,63 @@ export default function ReminderSheet({
/>
</View>
<Dialog context="local" />
<ScrollView>
<ScrollView
bounces={false}
style={{
marginBottom: DDS.isTab ? 25 : undefined
}}
>
{reminderMode === ReminderModes.Permanent ? null : (
<ScrollView
style={{
flexDirection: "row",
borderWidth: 1,
marginTop: 12,
borderRadius: 5,
borderColor: colors.primary.border,
paddingLeft: 12,
height: 50
}}
horizontal
>
{Object.keys(ReminderNotificationModes).map((mode) => (
<Button
key={mode}
title={mode}
style={{
marginRight: 12,
borderRadius: 100
}}
icon={
mode === "Silent"
? "minus-circle"
: mode === "Vibrate"
? "vibrate"
: "volume-high"
}
height={35}
type={
reminderNotificationMode ===
ReminderNotificationModes[
mode as keyof typeof ReminderNotificationModes
]
? "grayAccent"
: "gray"
}
onPress={() => {
const _mode = ReminderNotificationModes[
mode as keyof typeof ReminderNotificationModes
] as Reminder["priority"];
SettingsService.set({
reminderNotificationMode: _mode
});
setReminderNotificatioMode(_mode);
}}
/>
))}
</ScrollView>
)}
<Input
fwdRef={titleRef}
defaultValue={reminder?.title || referencedItem?.title}
@@ -275,15 +331,21 @@ export default function ReminderSheet({
}}
height={80}
wrapperStyle={{
marginBottom: 20
marginBottom: 12
}}
/>
<View
<ScrollView
style={{
flexDirection: "row",
marginBottom: 12
marginBottom: 12,
height: 50,
borderWidth: 1,
borderRadius: 5,
borderColor: colors.primary.border,
paddingLeft: 12
}}
horizontal
>
{Object.keys(ReminderModes).map((mode) => (
<Button
@@ -322,7 +384,7 @@ export default function ReminderSheet({
}}
/>
))}
</View>
</ScrollView>
{reminderMode === ReminderModes.Repeat ? (
<View
@@ -469,9 +531,7 @@ export default function ReminderSheet({
androidVariant="nativeAndroid"
is24hourSource="locale"
locale={
db.settings?.getTimeFormat() === "24-hour"
? "en_GB.UTF8"
: "en_US.UTF8"
db.settings?.getTimeFormat() === "24-hour" ? "en_GB" : "en_US"
}
mode={reminderMode === ReminderModes.Repeat ? "time" : "datetime"}
/>
@@ -493,53 +553,6 @@ export default function ReminderSheet({
</View>
)}
{reminderMode === ReminderModes.Permanent ? null : (
<View
style={{
flexDirection: "row",
marginBottom: 12,
paddingTop: 12,
borderTopWidth: 1,
borderTopColor: colors.secondary.background
}}
>
{Object.keys(ReminderNotificationModes).map((mode) => (
<Button
key={mode}
title={mode}
style={{
marginRight: 12,
borderRadius: 100
}}
icon={
mode === "Silent"
? "minus-circle"
: mode === "Vibrate"
? "vibrate"
: "volume-high"
}
height={35}
type={
reminderNotificationMode ===
ReminderNotificationModes[
mode as keyof typeof ReminderNotificationModes
]
? "grayAccent"
: "gray"
}
onPress={() => {
const _mode = ReminderNotificationModes[
mode as keyof typeof ReminderNotificationModes
] as Reminder["priority"];
SettingsService.set({
reminderNotificationMode: _mode
});
setReminderNotificatioMode(_mode);
}}
/>
))}
</View>
)}
{reminderMode === ReminderModes.Once ||
reminderMode === ReminderModes.Permanent ? null : (
<View
@@ -590,6 +603,11 @@ export default function ReminderSheet({
}}
/>
</ScrollView>
<View
style={{
height: 10
}}
/>
</View>
);
}
@@ -601,7 +619,7 @@ ReminderSheet.present = (
) => {
presentSheet({
context: isSheet ? "local" : undefined,
enableGesturesInScrollView: true,
enableGesturesInScrollView: false,
noBottomPadding: true,
component: (ref, close, update) => (
<ReminderSheet

View File

@@ -17,12 +17,14 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { EVENTS } from "@notesnook/core/common";
import { EVENTS } from "@notesnook/core/dist/common";
import React, { useCallback, useEffect, useRef, useState } from "react";
import { ActivityIndicator, Platform, View } from "react-native";
import { FlatList } from "react-native-actions-sheet";
import RNFetchBlob from "react-native-blob-util";
import DocumentPicker from "react-native-document-picker";
import DocumentPicker, {
DocumentPickerResponse
} from "react-native-document-picker";
import * as ScopedStorage from "react-native-scoped-storage";
import { db } from "../../../common/database";
import storage from "../../../common/database/storage";
@@ -45,6 +47,8 @@ import Seperator from "../../ui/seperator";
import SheetWrapper from "../../ui/sheet";
import Paragraph from "../../ui/typography/paragraph";
import { getFormattedDate } from "@notesnook/common";
import { unzip } from "react-native-zip-archive";
import { cacheDir, copyFileAsync } from "../../../common/filesystem/utils";
const RestoreDataSheet = () => {
const [visible, setVisible] = useState(false);
@@ -129,75 +133,71 @@ const RestoreDataComponent = ({ close, setRestoring, restoring }) => {
useEffect(() => {
setTimeout(() => {
checkBackups();
}, 300);
}, 1000);
}, []);
const restore = async (item) => {
if (restoring) {
return;
}
try {
setRestoring(true);
let prefix = Platform.OS === "ios" ? "" : "file:/";
let backup;
if (Platform.OS === "android") {
backup = await ScopedStorage.readFile(item.uri, "utf8");
} else {
backup = await RNFetchBlob.fs.readFile(prefix + item.path, "utf8");
}
backup = JSON.parse(backup);
if (backup.data.iv && backup.data.salt) {
withPassword(
async (value) => {
try {
await restoreBackup(backup, value);
close();
setRestoring(false);
return true;
} catch (e) {
backupError(e);
return false;
}
},
() => {
setRestoring(false);
try {
const file = Platform.OS === "ios" ? item.path : item.uri;
console.log(file);
if (file.endsWith(".nnbackupz")) {
setRestoring(true);
if (Platform.OS === "android") {
const cacheFile = `file://${RNFetchBlob.fs.dirs.CacheDir}/backup.zip`;
if (await RNFetchBlob.fs.exists(cacheFile)) {
await RNFetchBlob.fs.unlink(cacheFile);
}
);
} else {
await restoreBackup(backup);
close();
await RNFetchBlob.fs.createFile(cacheFile, "", "utf8");
console.log("copying");
await copyFileAsync(file, cacheFile);
console.log("copied");
await restoreFromZip(cacheFile);
} else {
await restoreFromZip(file, false);
}
} else if (file.endsWith(".nnbackup")) {
let backup;
if (Platform.OS === "android") {
backup = await ScopedStorage.readFile(file, "utf8");
} else {
backup = await RNFetchBlob.fs.readFile(file, "utf8");
}
await restoreFromNNBackup(JSON.parse(backup));
}
} catch (e) {
console.log("error", e);
setRestoring(false);
backupError(e);
}
};
const withPassword = (onsubmit, onclose = () => {}) => {
presentDialog({
context: "local",
title: "Encrypted backup",
input: true,
inputPlaceholder: "Password",
paragraph: "Please enter password of this backup file to restore it",
positiveText: "Restore",
secureTextEntry: true,
onClose: onclose,
negativeText: "Cancel",
positivePress: async (password) => {
try {
return await onsubmit(password);
} catch (e) {
ToastEvent.show({
heading: "Failed to backup data",
message: e.message,
type: "error",
context: "global"
});
return false;
const withPassword = () => {
return new Promise((resolve) => {
let resolved = false;
presentDialog({
context: "local",
title: "Encrypted backup",
input: true,
inputPlaceholder: "Password",
paragraph: "Please enter password of this backup file to restore it",
positiveText: "Restore",
secureTextEntry: true,
onClose: () => {
if (resolved) return;
resolve(undefined);
},
negativeText: "Cancel",
positivePress: async (password) => {
resolve(password);
resolved = true;
return true;
}
}
});
});
};
@@ -217,21 +217,164 @@ const RestoreDataComponent = ({ close, setRestoring, restoring }) => {
let path = await storage.checkAndCreateDir("/backups/");
files = await RNFetchBlob.fs.lstat(path);
}
files = files.sort(function (a, b) {
let timeA = a.lastModified;
let timeB = b.lastModified;
return timeB - timeA;
});
files = files
.filter((file) => {
const name = Platform.OS === "android" ? file.name : file.filename;
return name.endsWith(".nnbackup") || name.endsWith(".nnbackupz");
})
.sort(function (a, b) {
let timeA = a.lastModified;
let timeB = b.lastModified;
return timeB - timeA;
});
setFiles(files);
setTimeout(() => {
setLoading(false);
}, 1000);
setLoading(false);
} catch (e) {
console.log(e);
setLoading(false);
}
};
const restoreBackup = async (backup, password) => {
await db.backup.import(backup, password);
await db.initCollections();
initialize();
ToastEvent.show({
heading: "Backup restored successfully.",
type: "success",
context: "global"
});
return true;
};
const backupError = (e) => {
ToastEvent.show({
heading: "Restore failed",
message:
e.message ||
"The selected backup data file is invalid. You must select a *.nnbackup file to restore.",
type: "error",
context: "local"
});
};
/**
*
* @param {string} file
*/
async function restoreFromZip(file, remove) {
try {
const zipOutputFolder = `${cacheDir}/backup_extracted`;
if (await RNFetchBlob.fs.exists(zipOutputFolder)) {
await RNFetchBlob.fs.unlink(zipOutputFolder);
await RNFetchBlob.fs.mkdir(zipOutputFolder);
}
await unzip(file, zipOutputFolder);
console.log("Unzipped files successfully to", zipOutputFolder);
const backupFiles = await RNFetchBlob.fs.ls(zipOutputFolder);
if (backupFiles.findIndex((file) => file === ".nnbackup") === -1) {
throw new Error("Backup file is invalid");
}
let password;
console.log(`Found ${backupFiles?.length} files to restore from backup`);
for (const path of backupFiles) {
if (path === ".nnbackup") continue;
const filePath = `${zipOutputFolder}/${path}`;
const data = await RNFetchBlob.fs.readFile(filePath, "utf8");
const parsed = JSON.parse(data);
if (parsed.encrypted && !password) {
console.log("Backup is encrypted...", "requesting password");
password = await withPassword();
if (!password) throw new Error("Failed to decrypt backup");
}
await db.backup.import(parsed, password);
console.log("Imported", path);
}
// Remove files from cache
RNFetchBlob.fs.unlink(zipOutputFolder).catch(console.log);
if (remove) {
RNFetchBlob.fs.unlink(file).catch(console.log);
}
await db.initCollections();
initialize();
setRestoring(false);
close();
ToastEvent.show({
heading: "Backup restored successfully.",
type: "success",
context: "global"
});
} catch (e) {
backupError(e);
setRestoring(false);
}
}
/**
*
* @param {string} file
*/
async function restoreFromNNBackup(backup) {
try {
if (backup.data.iv && backup.data.salt) {
const password = await withPassword();
if (password) {
try {
await restoreBackup(backup, password);
close();
setRestoring(false);
} catch (e) {
setRestoring(false);
backupError(e);
}
} else {
setRestoring(false);
}
} else {
await restoreBackup(backup);
setRestoring(false);
close();
}
} catch (e) {
setRestoring(false);
backupError(e);
}
}
const button = {
title: "Restore from files",
onPress: async () => {
if (restoring) {
return;
}
try {
const file = await DocumentPicker.pickSingle({
copyTo: "cachesDirectory"
});
if (file.name.endsWith(".nnbackupz")) {
setRestoring(true);
await restoreFromZip(file.fileCopyUri, true);
} else if (file.name.endsWith(".nnbackup")) {
RNFetchBlob.fs.unlink(file.fileCopyUri).catch(console.log);
setRestoring(true);
const data = await fetch(file.uri);
await restoreFromNNBackup(await data.json());
}
} catch (e) {
console.log("error", e.stack);
setRestoring(false);
backupError(e);
}
}
};
const renderItem = ({ item, index }) => (
<View
style={{
@@ -271,76 +414,6 @@ const RestoreDataComponent = ({ close, setRestoring, restoring }) => {
</View>
);
const restoreBackup = async (backup, password) => {
await db.backup.import(backup, password);
setRestoring(false);
initialize();
ToastEvent.show({
heading: "Backup restored successfully.",
type: "success",
context: "global"
});
};
const backupError = (e) => {
ToastEvent.show({
heading: "Restore failed",
message:
e.message ||
"The selected backup data file is invalid. You must select a *.nnbackup file to restore.",
type: "error",
context: "local"
});
};
const button = {
title: "Restore from files",
onPress: () => {
if (restoring) {
return;
}
DocumentPicker.pickSingle()
.then((r) => {
setRestoring(true);
fetch(r.uri)
.then(async (r) => {
try {
let backup = await r.json();
if (backup.data.iv && backup.data.salt) {
withPassword(
async (value) => {
try {
restoreBackup(backup, value).then(() => {
close();
setRestoring(false);
});
return true;
} catch (e) {
backupError(e);
setRestoring(false);
return false;
}
},
() => {
setRestoring(false);
}
);
} else {
await restoreBackup(backup);
close();
}
} catch (e) {
setRestoring(false);
backupError(e);
}
})
.catch(console.log);
})
.catch(console.log);
}
};
return (
<>
<View>
@@ -377,7 +450,10 @@ const RestoreDataComponent = ({ close, setRestoring, restoring }) => {
height: 100
}}
>
<ActivityIndicator color={colors.primary.accent} size={SIZE.lg} />
<ActivityIndicator
color={colors.primary.accent}
size={SIZE.lg}
/>
</View>
) : (
<View
@@ -434,7 +510,9 @@ const RestoreDataComponent = ({ close, setRestoring, restoring }) => {
</Paragraph>
</>
) : (
<Paragraph color={colors.secondary.paragraph}>No backups found</Paragraph>
<Paragraph color={colors.secondary.paragraph}>
No backups found
</Paragraph>
)}
</View>
)

View File

@@ -16,6 +16,7 @@ GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { useThemeColors } from "@notesnook/theme";
import qclone from "qclone";
import React, {
createContext,
@@ -26,7 +27,7 @@ import React, {
useRef,
useState
} from "react";
import { RefreshControl, View } from "react-native";
import { RefreshControl, useWindowDimensions, View } from "react-native";
import ActionSheet, {
ActionSheetRef,
FlatList
@@ -45,7 +46,6 @@ import {
import useNavigationStore, {
NotebookScreenParams
} from "../../../stores/use-navigation-store";
import { useThemeColors } from "@notesnook/theme";
import {
eOnNewTopicAdded,
eOnTopicSheetUpdate,
@@ -54,18 +54,17 @@ import {
import { normalize, SIZE } from "../../../utils/size";
import { GroupHeader, NotebookType, TopicType } from "../../../utils/types";
import { groupArray } from "@notesnook/core/utils/grouping";
import { getTotalNotes } from "@notesnook/common";
import { groupArray } from "@notesnook/core/dist/utils/grouping";
import Config from "react-native-config";
import Icon from "react-native-vector-icons/MaterialCommunityIcons";
import { notesnook } from "../../../../e2e/test.ids";
import { MMKV } from "../../../common/database/mmkv";
import { openEditor } from "../../../screens/notes/common";
import { getTotalNotes } from "@notesnook/common";
import { useSelectionStore } from "../../../stores/use-selection-store";
import { deleteItems } from "../../../utils/functions";
import { presentDialog } from "../../dialog/functions";
import { Properties } from "../../properties";
import Sort from "../sort";
import { useSelectionStore } from "../../../stores/use-selection-store";
type ConfigItem = { id: string; type: string };
class TopicSheetConfig {
@@ -108,7 +107,8 @@ export const TopicsSheet = () => {
)
: []
);
const currentItem = useRef<string>();
const { fontScale } = useWindowDimensions();
const [groupOptions, setGroupOptions] = useState(
db.settings?.getGroupOptions("topics")
);
@@ -206,6 +206,11 @@ export const TopicsSheet = () => {
if (canShow) {
setTimeout(() => {
const id = isTopic ? currentScreen?.notebookId : currentScreen?.id;
if (currentItem.current !== id) {
setSelection([]);
setEnabled(false);
}
currentItem.current = id;
const notebook = db.notebooks?.notebook(id as string)?.data;
const snapPoint = isTopic
? 0
@@ -226,6 +231,8 @@ export const TopicsSheet = () => {
}
}, 300);
} else {
setSelection([]);
setEnabled(false);
ref.current?.hide();
}
}, [
@@ -263,6 +270,7 @@ export const TopicsSheet = () => {
index
);
}}
overlayColor={colors.primary.backdrop}
closable={!canShow}
elevation={10}
indicatorStyle={{
@@ -270,7 +278,7 @@ export const TopicsSheet = () => {
backgroundColor: colors.secondary.background
}}
keyboardHandlerEnabled={false}
snapPoints={Config.isTesting === "true" ? [100] : [25, 100]}
snapPoints={Config.isTesting === "true" ? [100] : [20, 100]}
initialSnapIndex={1}
backgroundInteractionEnabled
gestureEnabled
@@ -304,8 +312,8 @@ export const TopicsSheet = () => {
</View>
<View
style={{
maxHeight: 300,
height: 300,
maxHeight: 450,
height: 450,
width: "100%"
}}
>
@@ -328,30 +336,19 @@ export const TopicsSheet = () => {
{enabled ? (
<IconButton
customStyle={{
marginLeft: 10
marginLeft: 10,
width: 40 * fontScale,
height: 40 * fontScale
}}
onPress={async () => {
//@ts-ignore
useSelectionStore.setState({
selectedItemsList: selection
});
presentDialog({
title: `Delete ${
selection.length > 1 ? "topics" : "topics"
}`,
paragraph: `Are you sure you want to delete ${
selection.length > 1 ? "these topics?" : "this topic?"
}`,
positiveText: "Delete",
negativeText: "Cancel",
positivePress: async () => {
await deleteItems();
useSelectionStore.getState().clearSelection();
setEnabled(false);
setSelection([]);
},
positiveType: "errorShade"
});
await deleteItems();
useSelectionStore.getState().clearSelection();
setEnabled(false);
setSelection([]);
return;
}}
color={colors.primary.icon}
@@ -377,8 +374,8 @@ export const TopicsSheet = () => {
color={colors.primary.icon}
size={22}
customStyle={{
width: 40,
height: 40
width: 40 * fontScale,
height: 40 * fontScale
}}
/>
<IconButton
@@ -388,8 +385,8 @@ export const TopicsSheet = () => {
color={colors.primary.icon}
size={22}
customStyle={{
width: 40,
height: 40
width: 40 * fontScale,
height: 40 * fontScale
}}
/>
@@ -407,8 +404,8 @@ export const TopicsSheet = () => {
color={colors.primary.icon}
size={22}
customStyle={{
width: 40,
height: 40
width: 40 * fontScale,
height: 40 * fontScale
}}
/>
</>
@@ -482,6 +479,7 @@ const TopicItem = ({
selection.selection.findIndex((selected) => selected.id === item.id) > -1;
const isFocused = screen.id === item.id;
const notesCount = getTotalNotes(item);
const { fontScale } = useWindowDimensions();
return (
<PressableButton
@@ -518,6 +516,10 @@ const TopicItem = ({
<IconButton
size={SIZE.lg}
color={isSelected ? colors.selected.icon : colors.primary.icon}
top={0}
left={0}
bottom={0}
right={0}
name={
isSelected
? "check-circle-outline"
@@ -537,8 +539,8 @@ const TopicItem = ({
<IconButton
name="dots-horizontal"
customStyle={{
width: 40,
height: 40
width: 40 * fontScale,
height: 40 * fontScale
}}
testID={notesnook.ids.notebook.menu}
onPress={() => {

View File

@@ -28,7 +28,8 @@ import { useNoteStore } from "../../stores/use-notes-store";
import { useSettingStore } from "../../stores/use-setting-store";
import { useThemeColors } from "@notesnook/theme";
import { useUserStore } from "../../stores/use-user-store";
import { MenuItemsList, SUBSCRIPTION_STATUS } from "../../utils/constants";
import { MenuItemsList } from "../../utils/menu-items";
import { SUBSCRIPTION_STATUS } from "../../utils/constants";
import { eOpenPremiumDialog } from "../../utils/events";
import { ColorSection } from "./color-section";
import { MenuItem } from "./menu-item";

View File

@@ -17,20 +17,18 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import React, { useEffect, useState } from "react";
import { View } from "react-native";
import { useThemeColors } from "@notesnook/theme";
import React, { useCallback, useEffect, useState } from "react";
import { Dimensions, View } from "react-native";
import Icon from "react-native-vector-icons/MaterialCommunityIcons";
import ToggleSwitch from "toggle-switch-react-native";
import Navigation from "../../services/navigation";
import useNavigationStore from "../../stores/use-navigation-store";
import { useThemeColors } from "@notesnook/theme";
import { normalize, SIZE } from "../../utils/size";
import { SIZE, normalize } from "../../utils/size";
import { Button } from "../ui/button";
import { PressableButton } from "../ui/pressable";
import Heading from "../ui/typography/heading";
import Paragraph from "../ui/typography/paragraph";
import { useCallback } from "react";
import Tag from "../ui/tag";
export const MenuItem = React.memo(
function MenuItem({ item, index, testID, rightBtn }) {
@@ -40,6 +38,7 @@ export const MenuItem = React.memo(
);
const screenId = item.name.toLowerCase() + "_navigation";
let isFocused = headerTextState?.id === screenId;
const primaryColors = isFocused ? colors.selected : colors.primary;
const _onPress = () => {
if (item.func) {
@@ -110,6 +109,7 @@ export const MenuItem = React.memo(
textAlignVertical: "center",
textAlign: "left"
}}
allowFontScaling
name={item.icon}
color={
item.icon === "crown"
@@ -128,14 +128,28 @@ export const MenuItem = React.memo(
<Paragraph size={SIZE.md}>{item.name}</Paragraph>
)}
<Tag visible={item.isBeta} text="BETA" />
{item.isBeta ? (
<View
style={{
borderRadius: 100,
backgroundColor: primaryColors.accent,
paddingHorizontal: 4,
marginLeft: 5,
paddingVertical: 2
}}
>
<Paragraph color={primaryColors.accentForeground} size={SIZE.xxs}>
BETA
</Paragraph>
</View>
) : null}
</View>
{item.switch ? (
<ToggleSwitch
isOn={item.on}
onColor={colors.primary.accent}
offColor={colors.primary.icon}
onColor={primaryColors.accent}
offColor={primaryColors.icon}
size="small"
animationSpeed={150}
onToggle={_onPress}

View File

@@ -108,9 +108,12 @@ export const PinItem = React.memo(
alias = item?.alias || item?.title;
const [visible, setVisible] = useState(false);
const [headerTextState, setHeaderTextState] = useState(null);
const primaryColors =
headerTextState?.id === item.id ? colors.selected : colors.primary;
const color =
headerTextState?.id === item.id
? colors.primary.accent
? colors.selected.accent
: colors.primary.icon;
const fwdRef = useRef();
@@ -206,13 +209,19 @@ export const PinItem = React.memo(
justifyContent: "center"
}}
>
<Icon color={color} size={SIZE.lg - 2} name={icons[item.type]} />
<Icon
allowFontScaling
color={color}
size={SIZE.lg - 2}
name={icons[item.type]}
/>
<Icon
style={{
position: "absolute",
bottom: -6,
left: -6
}}
allowFontScaling
color={color}
size={SIZE.xs}
name="arrow-top-right-thick"
@@ -230,7 +239,7 @@ export const PinItem = React.memo(
style={{
flexWrap: "wrap"
}}
color={colors.primary.heading}
color={primaryColors.heading}
size={SIZE.md}
>
{alias}
@@ -238,7 +247,7 @@ export const PinItem = React.memo(
) : (
<Paragraph
numberOfLines={1}
color={colors.primary.paragraph}
color={primaryColors.paragraph}
size={SIZE.md}
>
{alias}

View File

@@ -113,6 +113,7 @@ export const UserStatus = () => {
<Icon
name="checkbox-blank-circle"
size={11}
allowFontScaling
color={
!user || lastSyncStatus === SyncStatus.Failed
? colors.error.icon
@@ -135,7 +136,7 @@ export const UserStatus = () => {
? "Last sync failed, tap to try again"
: syncing
? `Syncing your notes${
progress ? ` (${progress.current}/${progress.total})` : ""
progress ? ` (${progress.current})` : ""
}`
: "Tap here to sync your notes."}
</Paragraph>
@@ -149,9 +150,15 @@ export const UserStatus = () => {
color={colors.error.icon}
name="sync-alert"
size={SIZE.lg}
allowFontScaling
/>
) : (
<Icon color={colors.primary.accent} name="sync" size={SIZE.lg} />
<Icon
allowFontScaling
color={colors.primary.accent}
name="sync"
size={SIZE.lg}
/>
)
) : null}
</PressableButton>

View File

@@ -214,6 +214,10 @@ export const FluidTabs = forwardRef<TabsRef, TabProps>(function FluidTabs(
return;
}
translateX.value = animated ? withTiming(homePosition) : homePosition;
if (!animated) {
translateX.value = 299;
translateX.value = 300;
}
onDrawerStateChange(false);
isDrawerOpen.value = false;
},

View File

@@ -77,7 +77,9 @@ export const Tip = ({
alignSelf: "flex-start",
borderRadius: 100,
borderWidth: 1,
borderColor: colors.static[color as never] || colors.primary[color as never] as string
borderColor:
colors.static[color as never] ||
(colors.primary[color as never] as string)
}}
/>
@@ -139,7 +141,7 @@ export const Tip = ({
icon={tip.button.icon}
buttonType={{
color: colors.static[color as never],
text: colors.static.white
text: colors.primary.accentForeground
}}
style={{
marginTop: 10

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