Compare commits

...

220 Commits

Author SHA1 Message Date
alihamuh
b1c60c3a04 core: added note title to noteHistory for saving sessions 2024-03-07 09:35:39 +05:00
Ammar Ahmed
b3cc9475ad mobile: fix attachment search 2024-03-01 08:22:14 +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
278 changed files with 12079 additions and 38193 deletions

View File

@@ -1,4 +1,4 @@
name: Publish @notesnook/mobile
name: Publish @notesnook/android
on: workflow_dispatch
@@ -33,13 +33,15 @@ jobs:
- name: Sign app bundle for Playstore release
id: sign_app
uses: r0adkll/sign-android-release@v1
uses: r0adkll/sign-android-release@master
with:
releaseDirectory: apps/mobile/native/android/app/build/outputs/bundle/release
signingKeyBase64: ${{ secrets.SIGNING_KEY }}
alias: ${{ secrets.ALIAS }}
keyStorePassword: ${{ secrets.KEY_PASSWORD }}
keyPassword: ${{ secrets.KEY_PASSWORD }}
env:
BUILD_TOOLS_VERSION: "33.0.0"
- name: Publish to Playstore
id: deploy
@@ -64,6 +66,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:
@@ -163,6 +179,7 @@ jobs:
build-linux:
name: Build for Linux
needs: build
if: inputs.build-linux
runs-on: ubuntu-22.04
steps:
@@ -221,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
@@ -247,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

@@ -1,10 +1,11 @@
name: Publish @notesnook/web Beta
on:
on:
workflow_dispatch:
push:
branches:
- master
- '!v3-beta'
paths-ignore:
- "apps/mobile/**"
- "packages/editor-mobile/**"
@@ -22,6 +23,7 @@ on:
- synchronize
branches:
- master
- '!v3-beta'
paths-ignore:
- "apps/mobile/**"
- "packages/editor-mobile/**"

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' }}

View File

@@ -1,12 +1,12 @@
{
"name": "@notesnook/desktop",
"version": "2.6.7",
"version": "2.6.15",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@notesnook/desktop",
"version": "2.6.7",
"version": "2.6.15",
"hasInstallScript": true,
"license": "GPL-3.0-or-later",
"dependencies": {
@@ -24,8 +24,8 @@
"@types/node": "18.16.1",
"@types/yargs": "^17.0.24",
"chokidar": "^3.5.3",
"electron": "25.8.1",
"electron-builder": "^24.6.4",
"electron": "25.9.8",
"electron-builder": "^24.9.1",
"electron-builder-notarize": "^1.5.1",
"esbuild": "^0.17.19",
"tree-kill": "^1.2.2",
@@ -173,12 +173,11 @@
}
},
"node_modules/@electron/asar": {
"version": "3.2.4",
"resolved": "https://registry.npmjs.org/@electron/asar/-/asar-3.2.4.tgz",
"integrity": "sha512-lykfY3TJRRWFeTxccEKdf1I6BLl2Plw81H0bbp4Fc5iEc67foDCa5pjJQULVgo0wF+Dli75f3xVcdb/67FFZ/g==",
"version": "3.2.8",
"resolved": "https://registry.npmjs.org/@electron/asar/-/asar-3.2.8.tgz",
"integrity": "sha512-cmskk5M06ewHMZAplSiF4AlME3IrnnZhKnWbtwKVLRkdJkKyUVjMLhDIiPIx/+6zQWVlKX/LtmK9xDme7540Sg==",
"dev": true,
"dependencies": {
"chromium-pickle-js": "^0.2.0",
"commander": "^5.0.0",
"glob": "^7.1.6",
"minimatch": "^3.0.4"
@@ -216,6 +215,7 @@
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/@electron/get/-/get-2.0.2.tgz",
"integrity": "sha512-eFZVFoRXb3GFGd7Ak7W4+6jBl9wBtiZ4AaYOse97ej6mKj5tkyO0dUnUChs1IhJZtx1BENo4/p4WUTXpi6vT+g==",
"dev": true,
"dependencies": {
"debug": "^4.1.1",
"env-paths": "^2.2.0",
@@ -236,6 +236,7 @@
"version": "8.1.0",
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz",
"integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==",
"dev": true,
"dependencies": {
"graceful-fs": "^4.2.0",
"jsonfile": "^4.0.0",
@@ -249,6 +250,7 @@
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz",
"integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==",
"dev": true,
"optionalDependencies": {
"graceful-fs": "^4.1.6"
}
@@ -257,6 +259,7 @@
"version": "6.3.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
"dev": true,
"bin": {
"semver": "bin/semver.js"
}
@@ -265,6 +268,7 @@
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz",
"integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==",
"dev": true,
"engines": {
"node": ">= 4.0.0"
}
@@ -798,6 +802,7 @@
"version": "4.6.0",
"resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz",
"integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==",
"dev": true,
"engines": {
"node": ">=10"
},
@@ -809,6 +814,7 @@
"version": "4.0.6",
"resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz",
"integrity": "sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==",
"dev": true,
"dependencies": {
"defer-to-connect": "^2.0.0"
},
@@ -853,6 +859,7 @@
"version": "6.0.3",
"resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.3.tgz",
"integrity": "sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==",
"dev": true,
"dependencies": {
"@types/http-cache-semantics": "*",
"@types/keyv": "^3.1.4",
@@ -861,9 +868,9 @@
}
},
"node_modules/@types/debug": {
"version": "4.1.8",
"resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.8.tgz",
"integrity": "sha512-/vPO1EPOs306Cvhwv7KfVfYvOJqA/S/AXjaHQiJboCZzcNDb+TIJFN9/2C9DZ//ijSKWioNyUxD792QmDJ+HKQ==",
"version": "4.1.12",
"resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz",
"integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==",
"dev": true,
"dependencies": {
"@types/ms": "*"
@@ -881,26 +888,29 @@
"node_modules/@types/http-cache-semantics": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.1.tgz",
"integrity": "sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ=="
"integrity": "sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ==",
"dev": true
},
"node_modules/@types/keyv": {
"version": "3.1.4",
"resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.4.tgz",
"integrity": "sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==",
"dev": true,
"dependencies": {
"@types/node": "*"
}
},
"node_modules/@types/ms": {
"version": "0.7.31",
"resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.31.tgz",
"integrity": "sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA==",
"version": "0.7.34",
"resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.34.tgz",
"integrity": "sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==",
"dev": true
},
"node_modules/@types/node": {
"version": "18.16.1",
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.16.1.tgz",
"integrity": "sha512-DZxSZWXxFfOlx7k7Rv4LAyiMroaxa3Ly/7OOzZO8cBNho0YzAi4qlbrx8W27JGqG57IgR/6J7r+nOJWw6kcvZA=="
"integrity": "sha512-DZxSZWXxFfOlx7k7Rv4LAyiMroaxa3Ly/7OOzZO8cBNho0YzAi4qlbrx8W27JGqG57IgR/6J7r+nOJWw6kcvZA==",
"devOptional": true
},
"node_modules/@types/normalize-package-data": {
"version": "2.4.1",
@@ -922,6 +932,7 @@
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.0.tgz",
"integrity": "sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==",
"dev": true,
"dependencies": {
"@types/node": "*"
}
@@ -951,15 +962,16 @@
"version": "2.10.0",
"resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.0.tgz",
"integrity": "sha512-Cn6WYCm0tXv8p6k+A8PvbDG763EDpBoTzHdA+Q/MF6H3sapGjCm9NzoaJncJS9tUKSuCoDs9XHxYYsQDgxR6kw==",
"dev": true,
"optional": true,
"dependencies": {
"@types/node": "*"
}
},
"node_modules/7zip-bin": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/7zip-bin/-/7zip-bin-5.1.1.tgz",
"integrity": "sha512-sAP4LldeWNz0lNzmTird3uWfFDWWTeg6V/MsmyyLR9X1idwKBWIgt/ZvinqQldJm3LecKEs1emkbquO6PCiLVQ==",
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/7zip-bin/-/7zip-bin-5.2.0.tgz",
"integrity": "sha512-ukTPVhqG4jNzMro2qA9HSCSSVJN3aN7tlb+hfqYCt3ER0yWroeA2VR38MNrOHLQ/cVj+DaIMad0kFCtWWowh/A==",
"dev": true
},
"node_modules/agent-base": {
@@ -1041,9 +1053,9 @@
"dev": true
},
"node_modules/app-builder-lib": {
"version": "24.6.4",
"resolved": "https://registry.npmjs.org/app-builder-lib/-/app-builder-lib-24.6.4.tgz",
"integrity": "sha512-m9931WXb83teb32N0rKg+ulbn6+Hl8NV5SUpVDOVz9MWOXfhV6AQtTdftf51zJJvCQnQugGtSqoLvgw6mdF/Rg==",
"version": "24.9.1",
"resolved": "https://registry.npmjs.org/app-builder-lib/-/app-builder-lib-24.9.1.tgz",
"integrity": "sha512-Q1nYxZcio4r+W72cnIRVYofEAyjBd3mG47o+zms8HlD51zWtA/YxJb01Jei5F+jkWhge/PTQK+uldsPh6d0/4g==",
"dev": true,
"dependencies": {
"@develar/schema-utils": "~2.6.5",
@@ -1052,15 +1064,15 @@
"@electron/universal": "1.4.1",
"@malept/flatpak-bundler": "^0.4.0",
"@types/fs-extra": "9.0.13",
"7zip-bin": "~5.1.1",
"7zip-bin": "~5.2.0",
"async-exit-hook": "^2.0.1",
"bluebird-lst": "^1.0.9",
"builder-util": "24.5.0",
"builder-util-runtime": "9.2.1",
"builder-util": "24.8.1",
"builder-util-runtime": "9.2.3",
"chromium-pickle-js": "^0.2.0",
"debug": "^4.3.4",
"ejs": "^3.1.8",
"electron-publish": "24.5.0",
"electron-publish": "24.8.1",
"form-data": "^4.0.0",
"fs-extra": "^10.1.0",
"hosted-git-info": "^4.1.0",
@@ -1079,6 +1091,19 @@
"node": ">=14.0.0"
}
},
"node_modules/app-builder-lib/node_modules/builder-util-runtime": {
"version": "9.2.3",
"resolved": "https://registry.npmjs.org/builder-util-runtime/-/builder-util-runtime-9.2.3.tgz",
"integrity": "sha512-FGhkqXdFFZ5dNC4C+yuQB9ak311rpGAw+/ASz8ZdxwODCv1GGMWgLDeofRkdi0F3VCHQEWy/aXcJQozx2nOPiw==",
"dev": true,
"dependencies": {
"debug": "^4.3.4",
"sax": "^1.2.4"
},
"engines": {
"node": ">=12.0.0"
}
},
"node_modules/argparse": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
@@ -1103,9 +1128,9 @@
}
},
"node_modules/async": {
"version": "3.2.4",
"resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz",
"integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==",
"version": "3.2.5",
"resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz",
"integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==",
"dev": true
},
"node_modules/async-exit-hook": {
@@ -1191,6 +1216,7 @@
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/boolean/-/boolean-3.2.0.tgz",
"integrity": "sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw==",
"dev": true,
"optional": true
},
"node_modules/brace-expansion": {
@@ -1242,6 +1268,7 @@
"version": "0.2.13",
"resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz",
"integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==",
"dev": true,
"engines": {
"node": "*"
}
@@ -1265,16 +1292,16 @@
"dev": true
},
"node_modules/builder-util": {
"version": "24.5.0",
"resolved": "https://registry.npmjs.org/builder-util/-/builder-util-24.5.0.tgz",
"integrity": "sha512-STnBmZN/M5vGcv01u/K8l+H+kplTaq4PAIn3yeuufUKSpcdro0DhJWxPI81k5XcNfC//bjM3+n9nr8F9uV4uAQ==",
"version": "24.8.1",
"resolved": "https://registry.npmjs.org/builder-util/-/builder-util-24.8.1.tgz",
"integrity": "sha512-ibmQ4BnnqCnJTNrdmdNlnhF48kfqhNzSeqFMXHLIl+o9/yhn6QfOaVrloZ9YUu3m0k3rexvlT5wcki6LWpjTZw==",
"dev": true,
"dependencies": {
"@types/debug": "^4.1.6",
"7zip-bin": "~5.1.1",
"7zip-bin": "~5.2.0",
"app-builder-bin": "4.0.0",
"bluebird-lst": "^1.0.9",
"builder-util-runtime": "9.2.1",
"builder-util-runtime": "9.2.3",
"chalk": "^4.1.2",
"cross-spawn": "^7.0.3",
"debug": "^4.3.4",
@@ -1300,6 +1327,19 @@
"node": ">=12.0.0"
}
},
"node_modules/builder-util/node_modules/builder-util-runtime": {
"version": "9.2.3",
"resolved": "https://registry.npmjs.org/builder-util-runtime/-/builder-util-runtime-9.2.3.tgz",
"integrity": "sha512-FGhkqXdFFZ5dNC4C+yuQB9ak311rpGAw+/ASz8ZdxwODCv1GGMWgLDeofRkdi0F3VCHQEWy/aXcJQozx2nOPiw==",
"dev": true,
"dependencies": {
"debug": "^4.3.4",
"sax": "^1.2.4"
},
"engines": {
"node": ">=12.0.0"
}
},
"node_modules/busboy": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz",
@@ -1316,6 +1356,7 @@
"version": "5.0.4",
"resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz",
"integrity": "sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==",
"dev": true,
"engines": {
"node": ">=10.6.0"
}
@@ -1324,6 +1365,7 @@
"version": "7.0.4",
"resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.4.tgz",
"integrity": "sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg==",
"dev": true,
"dependencies": {
"clone-response": "^1.0.2",
"get-stream": "^5.1.0",
@@ -1396,9 +1438,9 @@
"dev": true
},
"node_modules/ci-info": {
"version": "3.8.0",
"resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.8.0.tgz",
"integrity": "sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw==",
"version": "3.9.0",
"resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz",
"integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==",
"dev": true,
"funding": [
{
@@ -1443,6 +1485,7 @@
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.3.tgz",
"integrity": "sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==",
"dev": true,
"dependencies": {
"mimic-response": "^1.0.0"
},
@@ -1586,6 +1629,7 @@
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz",
"integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==",
"dev": true,
"dependencies": {
"mimic-response": "^3.1.0"
},
@@ -1600,6 +1644,7 @@
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz",
"integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==",
"dev": true,
"engines": {
"node": ">=10"
},
@@ -1611,6 +1656,7 @@
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz",
"integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==",
"dev": true,
"engines": {
"node": ">=10"
}
@@ -1619,6 +1665,7 @@
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.0.tgz",
"integrity": "sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA==",
"dev": true,
"optional": true,
"dependencies": {
"has-property-descriptors": "^1.0.0",
@@ -1644,6 +1691,7 @@
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz",
"integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==",
"dev": true,
"optional": true
},
"node_modules/dir-compare": {
@@ -1679,14 +1727,14 @@
}
},
"node_modules/dmg-builder": {
"version": "24.6.4",
"resolved": "https://registry.npmjs.org/dmg-builder/-/dmg-builder-24.6.4.tgz",
"integrity": "sha512-BNcHRc9CWEuI9qt0E655bUBU/j/3wUCYBVKGu1kVpbN5lcUdEJJJeiO0NHK3dgKmra6LUUZlo+mWqc+OCbi0zw==",
"version": "24.9.1",
"resolved": "https://registry.npmjs.org/dmg-builder/-/dmg-builder-24.9.1.tgz",
"integrity": "sha512-huC+O6hvHd24Ubj3cy2GMiGLe2xGFKN3klqVMLAdcbB6SWMd1yPSdZvV8W1O01ICzCCRlZDHiv4VrNUgnPUfbQ==",
"dev": true,
"dependencies": {
"app-builder-lib": "24.6.4",
"builder-util": "24.5.0",
"builder-util-runtime": "9.2.1",
"app-builder-lib": "24.9.1",
"builder-util": "24.8.1",
"builder-util-runtime": "9.2.3",
"fs-extra": "^10.1.0",
"iconv-lite": "^0.6.2",
"js-yaml": "^4.1.0"
@@ -1695,6 +1743,19 @@
"dmg-license": "^1.0.11"
}
},
"node_modules/dmg-builder/node_modules/builder-util-runtime": {
"version": "9.2.3",
"resolved": "https://registry.npmjs.org/builder-util-runtime/-/builder-util-runtime-9.2.3.tgz",
"integrity": "sha512-FGhkqXdFFZ5dNC4C+yuQB9ak311rpGAw+/ASz8ZdxwODCv1GGMWgLDeofRkdi0F3VCHQEWy/aXcJQozx2nOPiw==",
"dev": true,
"dependencies": {
"debug": "^4.3.4",
"sax": "^1.2.4"
},
"engines": {
"node": ">=12.0.0"
}
},
"node_modules/dmg-license": {
"version": "1.0.11",
"resolved": "https://registry.npmjs.org/dmg-license/-/dmg-license-1.0.11.tgz",
@@ -1751,9 +1812,10 @@
}
},
"node_modules/electron": {
"version": "25.8.1",
"resolved": "https://registry.npmjs.org/electron/-/electron-25.8.1.tgz",
"integrity": "sha512-GtcP1nMrROZfFg0+mhyj1hamrHvukfF6of2B/pcWxmWkd5FVY1NJib0tlhiorFZRzQN5Z+APLPr7aMolt7i2AQ==",
"version": "25.9.8",
"resolved": "https://registry.npmjs.org/electron/-/electron-25.9.8.tgz",
"integrity": "sha512-PGgp6PH46QVENHuAHc2NT1Su8Q1qov7qIl2jI5tsDpTibwV2zD8539AeWBQySeBU4dhbj9onIl7+1bXQ0wefBg==",
"dev": true,
"hasInstallScript": true,
"dependencies": {
"@electron/get": "^2.0.0",
@@ -1768,16 +1830,16 @@
}
},
"node_modules/electron-builder": {
"version": "24.6.4",
"resolved": "https://registry.npmjs.org/electron-builder/-/electron-builder-24.6.4.tgz",
"integrity": "sha512-uNWQoU7pE7qOaIQ6CJHpBi44RJFVG8OHRBIadUxrsDJVwLLo8Nma3K/EEtx5/UyWAQYdcK4nVPYKoRqBb20hbA==",
"version": "24.9.1",
"resolved": "https://registry.npmjs.org/electron-builder/-/electron-builder-24.9.1.tgz",
"integrity": "sha512-v7BuakDuY6sKMUYM8mfQGrwyjBpZ/ObaqnenU0H+igEL10nc6ht049rsCw2HghRBdEwJxGIBuzs3jbEhNaMDmg==",
"dev": true,
"dependencies": {
"app-builder-lib": "24.6.4",
"builder-util": "24.5.0",
"builder-util-runtime": "9.2.1",
"app-builder-lib": "24.9.1",
"builder-util": "24.8.1",
"builder-util-runtime": "9.2.3",
"chalk": "^4.1.2",
"dmg-builder": "24.6.4",
"dmg-builder": "24.9.1",
"fs-extra": "^10.1.0",
"is-ci": "^3.0.0",
"lazy-val": "^1.0.5",
@@ -1839,6 +1901,19 @@
"integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==",
"dev": true
},
"node_modules/electron-builder/node_modules/builder-util-runtime": {
"version": "9.2.3",
"resolved": "https://registry.npmjs.org/builder-util-runtime/-/builder-util-runtime-9.2.3.tgz",
"integrity": "sha512-FGhkqXdFFZ5dNC4C+yuQB9ak311rpGAw+/ASz8ZdxwODCv1GGMWgLDeofRkdi0F3VCHQEWy/aXcJQozx2nOPiw==",
"dev": true,
"dependencies": {
"debug": "^4.3.4",
"sax": "^1.2.4"
},
"engines": {
"node": ">=12.0.0"
}
},
"node_modules/electron-notarize": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/electron-notarize/-/electron-notarize-1.2.2.tgz",
@@ -1869,20 +1944,33 @@
}
},
"node_modules/electron-publish": {
"version": "24.5.0",
"resolved": "https://registry.npmjs.org/electron-publish/-/electron-publish-24.5.0.tgz",
"integrity": "sha512-zwo70suH15L15B4ZWNDoEg27HIYoPsGJUF7xevLJLSI7JUPC8l2yLBdLGwqueJ5XkDL7ucYyRZzxJVR8ElV9BA==",
"version": "24.8.1",
"resolved": "https://registry.npmjs.org/electron-publish/-/electron-publish-24.8.1.tgz",
"integrity": "sha512-IFNXkdxMVzUdweoLJNXSupXkqnvgbrn3J4vognuOY06LaS/m0xvfFYIf+o1CM8if6DuWYWoQFKPcWZt/FUjZPw==",
"dev": true,
"dependencies": {
"@types/fs-extra": "^9.0.11",
"builder-util": "24.5.0",
"builder-util-runtime": "9.2.1",
"builder-util": "24.8.1",
"builder-util-runtime": "9.2.3",
"chalk": "^4.1.2",
"fs-extra": "^10.1.0",
"lazy-val": "^1.0.5",
"mime": "^2.5.2"
}
},
"node_modules/electron-publish/node_modules/builder-util-runtime": {
"version": "9.2.3",
"resolved": "https://registry.npmjs.org/builder-util-runtime/-/builder-util-runtime-9.2.3.tgz",
"integrity": "sha512-FGhkqXdFFZ5dNC4C+yuQB9ak311rpGAw+/ASz8ZdxwODCv1GGMWgLDeofRkdi0F3VCHQEWy/aXcJQozx2nOPiw==",
"dev": true,
"dependencies": {
"debug": "^4.3.4",
"sax": "^1.2.4"
},
"engines": {
"node": ">=12.0.0"
}
},
"node_modules/electron-trpc": {
"version": "0.5.2",
"resolved": "https://registry.npmjs.org/electron-trpc/-/electron-trpc-0.5.2.tgz",
@@ -1920,6 +2008,7 @@
"version": "1.4.4",
"resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
"integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==",
"dev": true,
"dependencies": {
"once": "^1.4.0"
}
@@ -1928,6 +2017,7 @@
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz",
"integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==",
"dev": true,
"engines": {
"node": ">=6"
}
@@ -1951,6 +2041,7 @@
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz",
"integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==",
"dev": true,
"optional": true
},
"node_modules/esbuild": {
@@ -2002,6 +2093,7 @@
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
"integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
"dev": true,
"optional": true,
"engines": {
"node": ">=10"
@@ -2027,6 +2119,7 @@
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz",
"integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==",
"dev": true,
"dependencies": {
"debug": "^4.1.1",
"get-stream": "^5.1.0",
@@ -2067,6 +2160,7 @@
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz",
"integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==",
"dev": true,
"dependencies": {
"pend": "~1.2.0"
}
@@ -2183,7 +2277,7 @@
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
"devOptional": true
"dev": true
},
"node_modules/get-caller-file": {
"version": "2.0.5",
@@ -2197,6 +2291,7 @@
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.1.tgz",
"integrity": "sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==",
"dev": true,
"optional": true,
"dependencies": {
"function-bind": "^1.1.1",
@@ -2212,6 +2307,7 @@
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz",
"integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==",
"dev": true,
"dependencies": {
"pump": "^3.0.0"
},
@@ -2280,6 +2376,7 @@
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/global-agent/-/global-agent-3.0.0.tgz",
"integrity": "sha512-PT6XReJ+D07JvGoxQMkT6qji/jVNfX/h364XHZOWeRzy64sSFr+xJ5OX7LI3b4MPQzdL4H8Y8M0xzPpsVMwA8Q==",
"dev": true,
"optional": true,
"dependencies": {
"boolean": "^3.0.1",
@@ -2297,6 +2394,7 @@
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz",
"integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==",
"dev": true,
"optional": true,
"dependencies": {
"define-properties": "^1.1.3"
@@ -2312,6 +2410,7 @@
"version": "11.8.6",
"resolved": "https://registry.npmjs.org/got/-/got-11.8.6.tgz",
"integrity": "sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g==",
"dev": true,
"dependencies": {
"@sindresorhus/is": "^4.0.0",
"@szmarczak/http-timer": "^4.0.5",
@@ -2341,7 +2440,7 @@
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
"integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
"devOptional": true,
"dev": true,
"dependencies": {
"function-bind": "^1.1.1"
},
@@ -2362,6 +2461,7 @@
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz",
"integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==",
"dev": true,
"optional": true,
"dependencies": {
"get-intrinsic": "^1.1.1"
@@ -2374,6 +2474,7 @@
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz",
"integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==",
"dev": true,
"optional": true,
"engines": {
"node": ">= 0.4"
@@ -2386,6 +2487,7 @@
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
"integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==",
"dev": true,
"optional": true,
"engines": {
"node": ">= 0.4"
@@ -2409,7 +2511,8 @@
"node_modules/http-cache-semantics": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz",
"integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ=="
"integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==",
"dev": true
},
"node_modules/http-proxy-agent": {
"version": "5.0.0",
@@ -2429,6 +2532,7 @@
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.3.tgz",
"integrity": "sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==",
"dev": true,
"dependencies": {
"quick-lru": "^5.1.1",
"resolve-alpn": "^1.0.0"
@@ -2691,7 +2795,8 @@
"node_modules/json-buffer": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
"integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ=="
"integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==",
"dev": true
},
"node_modules/json-parse-even-better-errors": {
"version": "2.3.1",
@@ -2709,6 +2814,7 @@
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
"integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==",
"dev": true,
"optional": true
},
"node_modules/json5": {
@@ -2738,6 +2844,7 @@
"version": "4.5.2",
"resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.2.tgz",
"integrity": "sha512-5MHbFaKn8cNSmVW7BYnijeAVlE4cYA/SVkifVgrh7yotnfhKmjuXpDKjrABLnT0SfHWV21P8ow07OGfRrNDg8g==",
"dev": true,
"dependencies": {
"json-buffer": "3.0.1"
}
@@ -2773,6 +2880,7 @@
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz",
"integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==",
"dev": true,
"engines": {
"node": ">=8"
}
@@ -2792,6 +2900,7 @@
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/matcher/-/matcher-3.0.0.tgz",
"integrity": "sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng==",
"dev": true,
"optional": true,
"dependencies": {
"escape-string-regexp": "^4.0.0"
@@ -2837,6 +2946,7 @@
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz",
"integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==",
"dev": true,
"engines": {
"node": ">=4"
}
@@ -2959,6 +3069,7 @@
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz",
"integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==",
"dev": true,
"engines": {
"node": ">=10"
},
@@ -2970,6 +3081,7 @@
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
"integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==",
"dev": true,
"optional": true,
"engines": {
"node": ">= 0.4"
@@ -2979,6 +3091,7 @@
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
"integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
"dev": true,
"dependencies": {
"wrappy": "1"
}
@@ -2987,6 +3100,7 @@
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz",
"integrity": "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==",
"dev": true,
"engines": {
"node": ">=8"
}
@@ -3066,7 +3180,8 @@
"node_modules/pend": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz",
"integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg=="
"integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==",
"dev": true
},
"node_modules/picomatch": {
"version": "2.3.1",
@@ -3105,6 +3220,7 @@
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz",
"integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==",
"dev": true,
"engines": {
"node": ">=0.4.0"
}
@@ -3126,6 +3242,7 @@
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
"integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==",
"dev": true,
"dependencies": {
"end-of-stream": "^1.1.0",
"once": "^1.3.1"
@@ -3144,6 +3261,7 @@
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz",
"integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==",
"dev": true,
"engines": {
"node": ">=10"
},
@@ -3338,12 +3456,14 @@
"node_modules/resolve-alpn": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz",
"integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g=="
"integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==",
"dev": true
},
"node_modules/responselike": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.1.tgz",
"integrity": "sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==",
"dev": true,
"dependencies": {
"lowercase-keys": "^2.0.0"
},
@@ -3379,6 +3499,7 @@
"version": "2.15.4",
"resolved": "https://registry.npmjs.org/roarr/-/roarr-2.15.4.tgz",
"integrity": "sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A==",
"dev": true,
"optional": true,
"dependencies": {
"boolean": "^3.0.1",
@@ -3458,12 +3579,14 @@
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz",
"integrity": "sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==",
"dev": true,
"optional": true
},
"node_modules/serialize-error": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-7.0.1.tgz",
"integrity": "sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==",
"dev": true,
"optional": true,
"dependencies": {
"type-fest": "^0.13.1"
@@ -3479,6 +3602,7 @@
"version": "0.13.1",
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.13.1.tgz",
"integrity": "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==",
"dev": true,
"optional": true,
"engines": {
"node": ">=10"
@@ -3599,6 +3723,7 @@
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.2.tgz",
"integrity": "sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==",
"dev": true,
"optional": true
},
"node_modules/stat-mode": {
@@ -3671,6 +3796,7 @@
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/sumchecker/-/sumchecker-3.0.1.tgz",
"integrity": "sha512-MvjXzkz/BOfyVDkG0oFOtBxHX2u3gKbMHIF/dXblZsgD3BWOFLmHovIpZY7BykJdAjcqRCBi1WYBNdEC9yI7vg==",
"dev": true,
"dependencies": {
"debug": "^4.1.0"
},
@@ -3940,7 +4066,8 @@
"node_modules/wrappy": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
"dev": true
},
"node_modules/xmlbuilder": {
"version": "15.1.1",
@@ -3993,6 +4120,7 @@
"version": "2.10.0",
"resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz",
"integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==",
"dev": true,
"dependencies": {
"buffer-crc32": "~0.2.3",
"fd-slicer": "~1.1.0"

View File

@@ -2,7 +2,7 @@
"name": "@notesnook/desktop",
"productName": "Notesnook",
"description": "Your private note taking space",
"version": "2.6.7",
"version": "2.6.15",
"appAppleId": "1544027013",
"private": true,
"main": "./dist/index.js",
@@ -25,8 +25,8 @@
"@types/node": "18.16.1",
"@types/yargs": "^17.0.24",
"chokidar": "^3.5.3",
"electron": "25.8.1",
"electron-builder": "^24.6.4",
"electron": "25.9.8",
"electron-builder": "^24.9.1",
"electron-builder-notarize": "^1.5.1",
"esbuild": "^0.17.19",
"tree-kill": "^1.2.2",
@@ -135,6 +135,10 @@
]
}
],
"signingHashAlgorithms": [
"sha256"
],
"sign": "./sign.js",
"icon": "assets/icons/app.ico"
},
"portable": {

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);
@@ -79,6 +83,7 @@ export const osIntegrationRouter = t.router({
AutoLaunch.disable();
}
config.desktopSettings = settings;
setupDesktopIntegration(settings);
}),
selectDirectory: t.procedure

View File

@@ -24,7 +24,6 @@ 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";
@@ -35,6 +34,7 @@ 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()) {
@@ -77,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"
@@ -93,6 +93,7 @@ async function createWindow() {
createIPCHandler({ router, windows: [mainWindow] });
globalThis.window = mainWindow;
mainWindow.setMenuBarVisibility(false);
mainWindowState.manage(mainWindow);
if (cliOptions.hidden && !config.desktopSettings.minimizeToSystemTray)
@@ -106,7 +107,7 @@ async function createWindow() {
}
await AssetManager.loadIcons();
setupDesktopIntegration();
setupDesktopIntegration(config.desktopSettings);
mainWindow.webContents.session.setSpellCheckerDictionaryDownloadURL(
"http://dictionaries.notesnook.com/"
@@ -176,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?.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 {
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

@@ -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

@@ -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,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 { protocol, net } from "electron";
import { protocol } from "electron";
import { isDevelopment } from "./index";
import { createReadStream } from "fs";
import { extname, normalize } from "path";
@@ -62,8 +62,25 @@ function registerProtocol() {
headers: { "Content-Type": extensionToMimeType[fileExtension] }
});
} else {
return net.fetch(request, {
bypassCustomProtocolHandlers: true
if (request.headers.has("X-Content-Length")) {
request.headers.set(
"Content-Length",
request.headers.get("X-Content-Length") || "0"
);
request.headers.delete("X-Content-Length");
}
const headers = Object.fromEntries(request.headers.entries());
return await fetch(request.url, {
signal: request.signal,
mode: request.mode,
headers,
method: request.method,
body: request.body,
credentials: request.credentials,
referrer: (request as any).referrer,
duplex: "half",
redirect: "manual"
});
}
});

View File

@@ -17,13 +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 "@azure/core-asynciterator-polyfill";
import SettingsService from "./services/settings";
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";
@@ -32,11 +31,16 @@ 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";
I18nManager.allowRTL(false);
I18nManager.forceRTL(false);
I18nManager.swapLeftAndRightInRTL(false);
SettingsService.checkOrientation();
const App = () => {

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 { MMKVLoader } from "react-native-mmkv-storage";
import { initalize } from "@notesnook/core/dist/logger";
import { MMKVLoader } from "react-native-mmkv-storage";
import { KV } from "./storage";
const LoggerStorage = new MMKVLoader()
@@ -26,4 +26,4 @@ const LoggerStorage = new MMKVLoader()
initalize(new KV(LoggerStorage));
export {};
export { LoggerStorage };

View File

@@ -90,6 +90,10 @@ export class KV {
return this.storage.removeItem(key);
}
async removeMulti(keys) {
return this.storage.removeItems(...keys);
}
async clear() {
return this.storage.clearStore();
}
@@ -145,6 +149,7 @@ export default {
clear: () => DefaultStorage.clear(),
getAllKeys: () => DefaultStorage.getAllKeys(),
writeMulti: (items) => DefaultStorage.writeMulti(items),
removeMulti: (keys) => DefaultStorage.removeMulti(keys),
encrypt,
decrypt,
decryptMulti,

View File

@@ -34,7 +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 } from "./io";
import { createCacheDir, exists } from "./io";
import { IOS_APPGROUPID } from "../../utils/constants";
export const FileDownloadStatus = {
Success: 1,
@@ -181,7 +182,9 @@ export default async function downloadAttachment(
silent: false,
cache: false,
throwError: false,
groupId: undefined
groupId: undefined,
base64: false,
text: false
}
) {
await createCacheDir();
@@ -212,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,
@@ -233,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

@@ -96,16 +96,22 @@ export async function downloadFile(filename, data, cancelToken) {
}
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

@@ -36,9 +36,13 @@ export async function readEncrypted(filename, key, cipherData) {
}
const attachment = db.attachments.attachment(filename);
const isPng = /(png)/g.test(attachment?.metadata.type);
const isJpeg = /(jpeg|jpg)/g.test(attachment?.metadata.type);
console.log("decrypting....");
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,
{

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

@@ -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

@@ -132,7 +132,7 @@ export const AttachmentItem = ({
activeOpacity={0.9}
onPress={() => {
if (encryption || !pressable) return;
db.fs.cancel(attachment.metadata.hash, currentProgress.type);
db.fs.cancel(attachment.metadata.hash);
setCurrentProgress(null);
}}
style={{

View File

@@ -49,7 +49,7 @@ export const AttachmentDialog = ({ note }) => {
const { height } = useSettingStore((state) => state.dimensions);
const [attachments, setAttachments] = useState(
note
? db.attachments.ofNote(note.id, "all")
? db.attachments.ofNote(note?.id, "all")
: [...(db.attachments.all || [])]
);
@@ -59,8 +59,8 @@ export const AttachmentDialog = ({ note }) => {
const [currentFilter, setCurrentFilter] = useState("all");
const onChangeText = (text) => {
const attachments = note
? db.attachments.ofNote(note.id, "all")
const attachments = note?.id
? db.attachments.ofNote(note?.id, "all")
: [...(db.attachments.all || [])];
attachmentSearchValue.current = text;
@@ -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,13 @@ export const AttachmentDialog = ({ note }) => {
}
];
const filterAttachments = (type) => {
const attachments = note
? db.attachments.ofNote(note.id, "all")
const filterAttachments = (type, _attachments) => {
const attachments = _attachments
? _attachments
: note
? db.attachments.ofNote(note?.id, "all")
: [...(db.attachments.all || [])];
isDocument;
switch (type) {
case "all":
return attachments;

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}
@@ -86,6 +95,7 @@ const AuthModal = () => {
transparent={false}
animated={false}
centered={false}
enableSheetKeyboardHandler
>
<KeyboardAwareScrollView
style={{

View File

@@ -66,6 +66,7 @@ const BaseDialog = ({
}, []);
useEffect(() => {
if (useUserStore.getState().disableAppLockRequests) return;
if (SettingsService.get().appLockMode === "background") {
if (appState === "background") {
setIntervalVisible(false);

View File

@@ -99,8 +99,8 @@ const NoteItem = ({
dateBy = "dateCreated",
noOpen = false
}) => {
const currentEditingNote = useEditorStore(
(state) => state.currentEditingNote
const isEditingNote = useEditorStore(
(state) => state.currentEditingNote === item.id
);
const { colors } = useThemeColors();
const compactMode = useIsCompactModeEnabled(item);
@@ -112,8 +112,7 @@ const NoteItem = ({
const reminder = getUpcomingReminder(reminders);
const noteColor = ColorValues[item.color?.toLowerCase()];
const tags = getTags(item);
const primaryColors =
currentEditingNote === item.id ? colors.selected : colors.primary;
const primaryColors = isEditingNote ? colors.selected : colors.primary;
return (
<>

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

@@ -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

@@ -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
@@ -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

@@ -101,6 +101,7 @@ const SheetProvider = ({ context = "global" }) => {
setVisible(false);
setData(null);
}}
keyboardHandlerDisabled={data?.keyboardHandlerDisabled}
bottomPadding={!data.noBottomPadding}
enableGesturesInScrollView={
typeof data.enableGesturesInScrollView === "undefined"

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

@@ -37,7 +37,7 @@ 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();
@@ -48,8 +48,7 @@ const PublishNoteSheet = ({ note: item, update }) => {
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();

View File

@@ -37,11 +37,11 @@ const SheetWrapper = ({
onOpen,
closeOnTouchBackdrop = true,
onHasReachedTop,
keyboardMode,
overlay,
overlayOpacity = 0.3,
enableGesturesInScrollView = false,
bottomPadding = true
bottomPadding = true,
keyboardHandlerDisabled
}) => {
const localRef = useRef(null);
const { colors } = useThemeColors("sheet");
@@ -85,6 +85,7 @@ const SheetWrapper = ({
};
useEffect(() => {
if (useUserStore.getState().disableAppLockRequests) return;
if (SettingsService.get().appLockMode === "background") {
if (appState === "background") {
const ref = fwdRef || localRef;
@@ -120,8 +121,9 @@ const SheetWrapper = ({
initialOffsetFromBottom={1}
onPositionChanged={onHasReachedTop}
closeOnTouchBackdrop={closeOnTouchBackdrop}
keyboardMode={keyboardMode}
keyboardHandlerEnabled={sheetKeyboardHandler}
keyboardHandlerEnabled={
keyboardHandlerDisabled ? false : sheetKeyboardHandler
}
closeOnPressBack={closeOnTouchBackdrop}
indicatorColor={colors.secondary.background}
onOpen={_onOpen}

View File

@@ -550,15 +550,6 @@ export const useActions = ({ close = () => null, item }) => {
}
async function exportNote() {
if (item.locked) {
ToastEvent.show({
heading: "Note is locked",
type: "error",
message: "Locked notes cannot be exported",
context: "local"
});
return;
}
ExportNotesSheet.present([item]);
}

View File

@@ -229,12 +229,25 @@ async function saveEditorState() {
MMKV.setString("appState", state);
}
}
/**
*
* @param {RNIap.Purchase} subscription
*/
const onSuccessfulSubscription = async (subscription) => {
console.log(
"Subscription success!",
subscription.transactionId,
subscription.obfuscatedAccountIdAndroid,
subscription.obfuscatedProfileIdAndroid
);
await PremiumService.subscriptions.set(subscription);
await PremiumService.subscriptions.verify(subscription);
};
/**
*
* @param {RNIap.PurchaseError} error
*/
const onSubscriptionError = async (error) => {
ToastEvent.show({
heading: "Failed to subscribe",
@@ -447,7 +460,6 @@ export const useAppEvents = () => {
}
clearMessage();
subscribeToIAPListeners();
if (!login) {
user = await db.user.fetchUser();
setUser(user);
@@ -466,7 +478,11 @@ export const useAppEvents = () => {
userEmailConfirmed: true
});
}
subscribeToIAPListeners();
} catch (e) {
DatabaseLogger.error(error);
ToastEvent.error(e, "An error occurred", "global");
}
@@ -490,14 +506,15 @@ export const useAppEvents = () => {
);
const subscribeToIAPListeners = useCallback(async () => {
await RNIap.initConnection()
.catch(() => null)
.then(async () => {
refValues.current.subsriptionSuccessListener =
RNIap.purchaseUpdatedListener(onSuccessfulSubscription);
refValues.current.subsriptionErrorListener =
RNIap.purchaseErrorListener(onSubscriptionError);
});
if (Platform.OS === "android") {
try {
await RNIap.flushFailedPurchasesCachedAsPendingAndroid();
} catch (e) {}
}
refValues.current.subsriptionSuccessListener =
RNIap.purchaseUpdatedListener(onSuccessfulSubscription);
refValues.current.subsriptionErrorListener =
RNIap.purchaseErrorListener(onSubscriptionError);
}, []);
const unSubscribeFromIAPListeners = () => {
@@ -606,15 +623,20 @@ export const useAppEvents = () => {
}, [loading, onUserUpdated]);
const initializeDatabase = useCallback(async () => {
if (!db.isInitialized) {
RNBootSplash.hide({ fade: true });
DatabaseLogger.info("Initializing database");
await db.init();
try {
if (!db.isInitialized) {
RNBootSplash.hide({ fade: true });
DatabaseLogger.info("Initializing database");
await db.init();
}
if (IsDatabaseMigrationRequired()) return;
initialize();
setLoading(false);
Walkthrough.init();
} catch (e) {
DatabaseLogger.error(e);
ToastEvent.error(e, "Error initializing database", "global");
}
if (IsDatabaseMigrationRequired()) return;
initialize();
setLoading(false);
Walkthrough.init();
}, [IsDatabaseMigrationRequired]);
useEffect(() => {

View File

@@ -1,82 +0,0 @@
/*
This file is part of the Notesnook project (https://notesnook.com/)
Copyright (C) 2023 Streetwriters (Private) Limited
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { useEffect, useState } from "react";
import { eSubscribeEvent, eUnSubscribeEvent } from "../services/event-manager";
import { useEditorStore } from "../stores/use-editor-store";
import { useTagStore } from "../stores/use-tag-store";
import { db } from "../common/database";
import { NoteType } from "app/utils/types";
import { useCallback } from "react";
/**
* A hook that injects/removes tags from tags bar in editor
*/
const useEditorTags = () => {
const currentEditingNote = useEditorStore(
(state) => state.currentEditingNote
);
const tags = useTagStore((state) => state.tags);
const [note, setNote] = useState<NoteType | null>(null);
const [noteTags, setNoteTags] = useState<string[]>([]);
const refreshNote = useCallback(() => {
const current = useEditorStore.getState().currentEditingNote;
if (!current) {
setNote(null);
setNoteTags([]);
return;
}
const note = db.notes?.note(current)?.data as NoteType;
setNote(note ? { ...note } : null);
getTags(note);
}, []);
useEffect(() => {
refreshNote();
}, [currentEditingNote, refreshNote, tags]);
const load = useCallback(() => {
if (!note) return;
// tiny.call(EditorWebView, renderTags(noteTags));
}, [note]);
useEffect(() => {
eSubscribeEvent("updateTags", load);
return () => {
eUnSubscribeEvent("updateTags", load);
};
}, [load, noteTags]);
function getTags(note: NoteType) {
if (!note || !note.tags) return [];
const tags = note.tags
.map((t) => (db.tags?.tag(t) ? { ...db.tags.tag(t) } : null))
.filter((t) => t !== null);
setNoteTags(tags);
}
useEffect(() => {
load();
}, [load, noteTags]);
return [];
};
export default useEditorTags;

View File

@@ -31,7 +31,8 @@
"url": "^0.11.0",
"validator": "^13.5.2",
"zustand": "^3.6.0",
"katex": "0.16.2"
"katex": "0.16.2",
"@readme/data-urls": "3.0.0"
},
"sideEffects": false
}

View File

@@ -45,6 +45,7 @@ import { EditorProps, useEditorType } from "./tiptap/types";
import { useEditor } from "./tiptap/use-editor";
import { useEditorEvents } from "./tiptap/use-editor-events";
import { editorController } from "./tiptap/utils";
import { useThemeColors } from "@notesnook/theme";
const style: ViewStyle = {
height: "100%",
@@ -93,35 +94,6 @@ const Editor = React.memo(
get: () => editor
}));
const onMediaDownloaded = useCallback(
({
hash,
groupId,
src,
attachmentType
}: {
hash: string;
groupId: string;
src: string;
attachmentType: string;
}) => {
if (groupId !== editor.note.current?.id) return;
editorController.current.markImageLoaded(hash);
if (attachmentType === "webclip") {
editor.commands.updateWebclip({
hash: hash,
src: src
});
} else {
editor.commands.updateImage({
hash: hash,
src: src
});
}
},
[editor.commands, editor.note]
);
const onError = useCallback(() => {
renderKey.current =
renderKey.current === `editor-0` ? `editor-1` : `editor-0`;
@@ -130,16 +102,11 @@ const Editor = React.memo(
}, [editor]);
useEffect(() => {
const sub = [
eSubscribeEvent("webview_reset", onError),
EV.subscribe(EVENTS.mediaAttachmentDownloaded, onMediaDownloaded)
];
const sub = [eSubscribeEvent("webview_reset", onError)];
return () => {
sub.forEach((s) => s.unsubscribe());
EV.unsubscribe(EVENTS.mediaAttachmentDownloaded, onMediaDownloaded);
};
}, [onError, onMediaDownloaded]);
}, [onError]);
useLayoutEffect(() => {
setImmediate(() => {
@@ -210,6 +177,7 @@ export default Editor;
const ReadonlyButton = ({ editor }: { editor: useEditorType }) => {
const readonly = useEditorStore((state) => state.readonly);
const keyboard = useKeyboard();
const { colors } = useThemeColors();
const onPress = async () => {
if (editor.note.current) {
@@ -225,7 +193,7 @@ const ReadonlyButton = ({ editor }: { editor: useEditorType }) => {
name="pencil-lock"
type="grayBg"
onPress={onPress}
color="accent"
color={colors.primary.accent}
customStyle={{
position: "absolute",
bottom: 60,

View File

@@ -17,10 +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 type {
Attachment,
AttachmentProgress
} from "@notesnook/editor/dist/extensions/attachment/index";
import type { Attachment } from "@notesnook/editor/dist/extensions/attachment/index";
import type { ImageAttributes } from "@notesnook/editor/dist/extensions/image/index";
import { createRef, RefObject } from "react";
import { Platform } from "react-native";
@@ -203,17 +200,33 @@ typeof globalThis.statusBar !== "undefined" && statusBar.current.set({date:"",sa
);
};
setAttachmentProgress = async (attachmentProgress: AttachmentProgress) => {
setAttachmentProgress = async (attachmentProgress: Partial<Attachment>) => {
await this.doAsync(
`editor && editor.commands.setAttachmentProgress(${JSON.stringify(
`editor && editor.commands.updateAttachment(${JSON.stringify(
attachmentProgress
)})`
)}, {
preventUpdate: true,
query: (attachment) => {
return attachment.hash === "${attachmentProgress.hash}";
}
})`
);
};
insertImage = async (image: ImageAttributes) => {
insertImage = async (
image: Omit<ImageAttributes, "bloburl"> & {
dataurl: string;
}
) => {
await this.doAsync(
`editor && editor.commands.insertImage(${JSON.stringify(image)})`
`const image = toBlobURL("${image.dataurl}", "${image.hash}");
editor && editor.commands.insertImage({
...${JSON.stringify({
...image,
dataurl: undefined
})},
bloburl: image
})`
);
};
@@ -225,11 +238,20 @@ typeof globalThis.statusBar !== "undefined" && statusBar.current.set({date:"",sa
);
};
updateImage = async ({ src, hash }: Partial<ImageAttributes>) => {
updateImage = async ({
hash,
dataurl
}: Partial<Omit<ImageAttributes, "bloburl">> & {
dataurl: string;
}) => {
await this.doAsync(
`editor && editor.commands.updateImage(${JSON.stringify({
`const image = toBlobURL("${dataurl}", "${hash}");
editor && editor.commands.updateImage(${JSON.stringify({
hash
})},${JSON.stringify({ dataurl: src, hash, preventUpdate: true })})`
})}, {
...${JSON.stringify({ hash, preventUpdate: true })},
bloburl: image
})`
);
};

View File

@@ -36,5 +36,6 @@ export const EventTypes = {
contentchange: "editor-event:content-change",
reminders: "editor-event:reminders",
previewAttachment: "editor-event:preview-attachment",
copyToClipboard: "editor-events:copy-to-clipboard"
copyToClipboard: "editor-events:copy-to-clipboard",
getAttachmentData: "editor-events:get-attachment-data"
};

View File

@@ -35,6 +35,7 @@ import { FILE_SIZE_LIMIT, IMAGE_SIZE_LIMIT } from "../../../utils/constants";
import { eCloseSheet } from "../../../utils/events";
import { editorController, editorState } from "./utils";
import { useSettingStore } from "../../../stores/use-setting-store";
import filesystem from "../../../common/filesystem";
const showEncryptionSheet = (file) => {
presentSheet({
@@ -230,6 +231,16 @@ const handleImageResponse = async (response, options) => {
});
let fileName = image.originalFileName || image.fileName;
editorController.current?.commands.insertImage({
hash: hash,
mime: image.type,
title: fileName,
dataurl: b64,
size: image.fileSize,
filename: fileName
});
if (!(await attachFile(uri, hash, image.type, fileName, options))) return;
const isPng = /(png)/g.test(image.type);
const isJpeg = /(jpeg|jpg)/g.test(image.type);
@@ -243,15 +254,6 @@ const handleImageResponse = async (response, options) => {
}
if (Platform.OS === "ios") await RNFetchBlob.fs.unlink(uri);
editorController.current?.commands.insertImage({
hash: hash,
mime: image.type,
title: fileName,
dataurl: b64,
size: image.fileSize,
filename: fileName
});
};
export async function attachFile(uri, hash, type, filename, options) {
@@ -269,6 +271,10 @@ export async function attachFile(uri, hash, type, filename, options) {
return false;
}
if (!options.reupload && exists) {
options.reupload = (await filesystem.getUploadedFileSize(hash)) <= 0;
}
if (!exists || options?.reupload) {
let key = await db.attachments.generateKey();
encryptionInfo = await Sodium.encryptFile(key, {

View File

@@ -97,6 +97,7 @@ export type SavePayload = {
type?: Content["type"];
sessionId?: string | null;
sessionHistoryId?: number;
ignoreEdit: boolean;
};
export type AppState = {

View File

@@ -66,6 +66,7 @@ import { EditorMessage, EditorProps, useEditorType } from "./types";
import { EditorEvents, editorState } from "./utils";
import { useNoteStore } from "../../../stores/use-notes-store";
import SettingsService from "../../../services/settings";
import downloadAttachment from "../../../common/filesystem/download-attachment";
const publishNote = async (editor: useEditorType) => {
const user = useUserStore.getState().user;
@@ -127,6 +128,8 @@ const showActionsheet = async (editor: useEditorType) => {
}
};
type ContentMessage = { html: string; ignoreEdit: boolean };
export const useEditorEvents = (
editor: useEditorType,
{ readonly: editorPropReadonly, noHeader, noToolbar }: Partial<EditorProps>
@@ -189,7 +192,7 @@ export const useEditorEvents = (
readonly: readonly || editorPropReadonly,
tools: tools || getDefaultPresets().default,
noHeader: noHeader,
noToolbar: readonly || editorPropReadonly || noToolbar,
noToolbar: noToolbar,
doubleSpacedLines: doubleSpacedLines,
corsProxy: corsProxy,
fontSize:
@@ -330,14 +333,16 @@ export const useEditorEvents = (
if (editorMessage.type === EventTypes.content) {
editor.saveContent({
type: editorMessage.type,
content: editorMessage.value as string,
content: (editorMessage.value as ContentMessage).html,
ignoreEdit: (editorMessage.value as ContentMessage).ignoreEdit,
forSessionId: editorMessage.sessionId
});
} else if (editorMessage.type === EventTypes.title) {
editor.saveContent({
type: editorMessage.type,
title: editorMessage.value as string,
forSessionId: editorMessage.sessionId
forSessionId: editorMessage.sessionId,
ignoreEdit: false
});
}
@@ -416,6 +421,44 @@ export const useEditorEvents = (
break;
}
case EventTypes.getAttachmentData: {
const attachment = (editorMessage.value as any)
.attachment as Attachment;
console.log(
"Getting attachment data:",
attachment.hash,
attachment.type
);
downloadAttachment(attachment.hash, true, {
base64: attachment.type === "image",
text: attachment.type === "web-clip",
silent: true,
groupId: editor.note.current?.id,
cache: true
} as any)
.then((data: any) => {
console.log(
"Got attachment data:",
!!data,
(editorMessage.value as any).resolverId
);
editor.postMessage(EditorEvents.attachmentData, {
resolverId: (editorMessage.value as any).resolverId,
data
});
})
.catch(() => {
console.log("Error downloading attachment data");
editor.postMessage(EditorEvents.attachmentData, {
resolverId: (editorMessage.value as any).resolverId,
data: undefined
});
});
break;
}
case EventTypes.pro:
if (editor.state.current?.isFocused) {
editor.state.current.isFocused = true;

View File

@@ -78,7 +78,6 @@ export const useEditor = (
const saveCount = useRef(0);
const lastContentChangeTime = useRef<number>(0);
const lock = useRef(false);
const loadedImages = useRef<{ [name: string]: boolean }>({});
const lockedSessionId = useRef<string>();
const loadingState = useRef<string>();
@@ -129,12 +128,9 @@ export const useEditor = (
const reset = useCallback(
async (resetState = true, resetContent = true) => {
currentNote.current?.id &&
db.fs?.cancel(currentNote.current.id, "download");
currentNote.current?.id && db.fs?.cancel(currentNote.current.id);
currentNote.current = null;
loadedImages.current = {};
currentContent.current = null;
clearTimeout(timers.current["loading-images"]);
sessionHistoryId.current = undefined;
saveCount.current = 0;
loadingState.current = undefined;
@@ -166,7 +162,8 @@ export const useEditor = (
data,
type,
sessionId: currentSessionId,
sessionHistoryId: currentSessionHistoryId
sessionHistoryId: currentSessionHistoryId,
ignoreEdit
}: SavePayload) => {
if (
readonly ||
@@ -198,6 +195,12 @@ export const useEditor = (
};
noteData.title = title;
if (ignoreEdit) {
console.log("Ignoring edits...");
noteData.dateEdited = note?.dateEdited;
}
if (data) {
noteData.content = {
data: data,
@@ -287,58 +290,6 @@ export const useEditor = (
}
}, []);
const getMediaToLoad = (previousContent?: string) => {
if (!currentNote.current?.id) return [];
const previousAttachments =
previousContent?.matchAll(/data-hash="(.+?)"/gm) || [];
const attachments =
currentContent.current?.data?.matchAll(/data-hash="(.+?)"/gm) || [];
const media: string[] = [];
const oldMatches = Array.from(previousAttachments).map((match) => match[1]);
const matches = Array.from(attachments).map((match) => match[1]);
for (let i = 0; i < matches.length; i++) {
const currentHash = matches[i];
const oldHash = oldMatches[i];
if (currentHash !== oldHash) {
media.push(currentHash);
loadedImages.current[currentHash] = false;
}
}
return media;
};
const markImageLoaded = (hash: string) => {
const attachment = loadedImages.current[hash];
if (typeof attachment === "boolean") {
loadedImages.current[hash] = true;
}
};
const loadImages = useCallback((previousContent?: string) => {
if (!currentNote.current?.id) return;
const timerId = "loading-images";
clearTimeout(timers.current[timerId]);
timers.current[timerId] = setTimeout(() => {
if (!currentNote.current?.id) return;
if (currentNote.current?.content?.isPreview) {
db.content?.downloadMedia(
currentNote.current?.id,
currentNote.current.content,
true
);
} else {
const media = getMediaToLoad(previousContent);
if (media.length > 0) {
db.attachments?.downloadMedia(currentNote.current?.id, media);
}
}
}, 1000);
}, []);
const loadNote = useCallback(
async (
item: Omit<NoteType, "type"> & {
@@ -408,13 +359,13 @@ export const useEditor = (
await commands.setStatus(getFormattedDate(item.dateEdited), "Saved");
await postMessage(EditorEvents.title, item.title);
loadingState.current = currentContent.current?.data;
if (currentContent.current?.data) {
await postMessage(
EditorEvents.html,
currentContent.current?.data,
10000
);
}
await postMessage(
EditorEvents.html,
currentContent.current?.data || "",
10000
);
loadingState.current = undefined;
useEditorStore.getState().setReadonly(item.readonly);
await commands.setTags(currentNote.current);
@@ -425,18 +376,9 @@ export const useEditor = (
}
}, 300);
overlay(false);
loadImages();
}
},
[
commands,
isDefaultEditor,
loadContent,
loadImages,
overlay,
postMessage,
reset
]
[commands, isDefaultEditor, loadContent, overlay, postMessage, reset]
);
const lockNoteWithVault = useCallback((note: NoteType) => {
@@ -466,8 +408,6 @@ export const useEditor = (
lock.current = true;
const previousContent = currentContent.current?.data;
if (data.type === "tiptap") {
if (!currentNote.current.locked && isContentEncrypted) {
lockNoteWithVault(note);
@@ -500,18 +440,8 @@ export const useEditor = (
}
lock.current = false;
if (data.type === "tiptap") {
loadImages(previousContent);
db.eventManager.subscribe(
EVENTS.syncCompleted,
() => {
loadImages(previousContent);
},
true
);
}
},
[loadImages, lockNoteWithVault, postMessage, commands]
[lockNoteWithVault, postMessage, commands]
);
useEffect(() => {
@@ -531,12 +461,14 @@ export const useEditor = (
title,
content,
type,
forSessionId
forSessionId,
ignoreEdit
}: {
title?: string;
content?: string;
type: string;
forSessionId: string;
ignoreEdit: boolean;
}) => {
if (lock.current || lockedSessionId.current === forSessionId) return;
lastContentChangeTime.current = Date.now();
@@ -567,7 +499,8 @@ export const useEditor = (
type: "tiptap",
sessionId: forSessionId,
id: noteId,
sessionHistoryId: sessionHistoryId.current
sessionHistoryId: sessionHistoryId.current,
ignoreEdit
};
withTimer(
noteId || "newnote",
@@ -585,7 +518,7 @@ export const useEditor = (
}
saveNote(params);
},
150
ignoreEdit ? 0 : 150
);
},
[withTimer, onChange, saveNote]
@@ -689,7 +622,6 @@ export const useEditor = (
saveContent,
onContentChanged,
editorId: editorId,
markImageLoaded,
overlay,
postMessage
};

View File

@@ -50,7 +50,8 @@ export const EditorEvents: { [name: string]: string } = {
titleplaceholder: "native:titleplaceholder",
logger: "native:logger",
status: "native:status",
keyboardShown: "native:keyboardShown"
keyboardShown: "native:keyboardShown",
attachmentData: "native:attachment-data"
};
export function randId(prefix: string) {

View File

@@ -115,7 +115,7 @@ export const EditorWrapper = ({ width }) => {
style={{ height: 1, padding: 0, width: 1, position: "absolute" }}
blurOnSubmit={false}
/>
<ProgressBar />
{/* <ProgressBar /> */}
<Editor key="editor" withController={true} />
</KeyboardAvoidingViewIOS>
)}

View File

@@ -69,10 +69,10 @@ export const TrashIntervalPicker = createSettingsPicker({
db.settings.setTrashCleanupInterval(item);
},
formatValue: (item) => {
return item === -1 ? "Never" : item + " days";
return item === -1 ? "Never" : item === 1 ? "Daily" : item + " days";
},
getItemKey: (item) => item.toString(),
options: [-1, 7, 30, 365],
options: [-1, 1, 7, 30, 365],
compareValue: (current, item) => current === item,
premium: true
});

View File

@@ -88,8 +88,8 @@ export const settingsGroups: SettingSection[] = [
return isBasic || !user.subscription?.type
? "Subscribe to Pro"
: isTrial
? "Your free trial has started"
: "Subscription details";
? "Your free trial has started"
: "Subscription details";
},
type: "component",
component: "subscription",
@@ -111,16 +111,16 @@ export const settingsGroups: SettingSection[] = [
return user.subscription?.type === 2
? "You signed up on " + startDate
: user.subscription?.type === 1
? "Your free trial will end on " + expiryDate
: user.subscription?.type === 6
? subscriptionDaysLeft.time < -3
? "Your subscription has ended"
: "Your account will be downgraded to Basic in 3 days"
: user.subscription?.type === 7
? `Your subscription will end on ${expiryDate}.`
: user.subscription?.type === 5
? `Your subscription will renew on ${expiryDate}.`
: "Never hesitate to choose privacy";
? "Your free trial will end on " + expiryDate
: user.subscription?.type === 6
? subscriptionDaysLeft.time < -3
? "Your subscription has ended"
: "Your account will be downgraded to Basic in 3 days"
: user.subscription?.type === 7
? `Your subscription will end on ${expiryDate}.`
: user.subscription?.type === 5
? `Your subscription will renew on ${expiryDate}.`
: "Never hesitate to choose privacy";
}
},
{
@@ -892,16 +892,6 @@ export const settingsGroups: SettingSection[] = [
name: "Restore backup",
description: "Restore backup from phone storage.",
modifer: () => {
const user = useUserStore.getState().user;
if (!user || !user?.email) {
ToastEvent.show({
heading: "Login required",
message: "Please log in to your account to restore backup",
type: "error",
context: "global"
});
return;
}
eSendEvent(eOpenRestoreDialog);
}
},

View File

@@ -42,6 +42,9 @@ export const Subscription = () => {
user?.subscription?.type !== SUBSCRIPTION_STATUS.PREMIUM &&
user?.subscription?.type !== SUBSCRIPTION_STATUS.BETA;
const hasCancelledPremium =
SUBSCRIPTION_STATUS.PREMIUM_CANCELLED === user?.subscription?.type;
const subscriptionProviderInfo =
SUBSCRIPTION_PROVIDER[user?.subscription?.provider];
@@ -63,10 +66,7 @@ export const Subscription = () => {
return;
}
if (
user?.subscription?.type === SUBSCRIPTION_STATUS.PREMIUM_CANCELLED &&
Platform.OS === "android"
) {
if (hasCancelledPremium && Platform.OS === "android") {
if (user.subscription?.provider === 3) {
ToastEvent.show({
heading: "Subscribed on web",
@@ -88,6 +88,13 @@ export const Subscription = () => {
}
};
function getPrice() {
return Platform.OS === "android"
? monthlyPlan?.product?.subscriptionOfferDetails[0].pricingPhases
.pricingPhaseList?.[0]?.formattedPrice
: monthlyPlan?.product?.localizedPrice;
}
return (
<View>
{isNotPro ? (
@@ -104,22 +111,16 @@ export const Subscription = () => {
title={
!user?.isEmailConfirmed
? "Confirm your email"
: user.subscription?.provider === 3 &&
user.subscription?.type ===
SUBSCRIPTION_STATUS.PREMIUM_CANCELLED
: user.subscription?.provider === 3 && hasCancelledPremium
? "Manage subscription from desktop app"
: user.subscription?.type ===
SUBSCRIPTION_STATUS.PREMIUM_CANCELLED &&
: hasCancelledPremium &&
Platform.OS === "android" &&
Config.GITHUB_RELEASE !== "true"
? "Resubscribe from Google Playstore"
: user.subscription?.type === SUBSCRIPTION_STATUS.PREMIUM_EXPIRED
? `Resubscribe to Pro (${
monthlyPlan?.product?.localizedPrice || "$4.49"
} / mo)`
: `Get Pro (${
monthlyPlan?.product?.localizedPrice || "$4.49"
} / mo)`
: user.subscription?.type ===
SUBSCRIPTION_STATUS.PREMIUM_EXPIRED || hasCancelledPremium
? `Resubscribe to Pro (${getPrice() || "$4.49"} / mo)`
: `Get Pro (${getPrice() || "$4.49"} / mo)`
}
/>
) : null}

View File

@@ -65,7 +65,11 @@ const PLACEHOLDER_DATA = (trashCleanupInterval = 7) => ({
paragraph:
trashCleanupInterval === -1
? "Set automatic trash cleanup interval from Settings > Behaviour > Clean trash interval."
: `Items in the trash will be permanently deleted after after ${trashCleanupInterval} days.`,
: `Trash gets automatically cleaned up ${
trashCleanupInterval === 1
? "daily."
: `after ${trashCleanupInterval} days.`
}`,
button: null,
loading: "Loading trash items"
});

View File

@@ -18,17 +18,19 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { decode, EntityLevel } from "entities";
import { zipSync } from "fflate";
import { Platform } from "react-native";
import RNFetchBlob from "react-native-blob-util";
import RNHTMLtoPDF from "react-native-html-to-pdf-lite";
import * as ScopedStorage from "react-native-scoped-storage";
import RNFetchBlob from "react-native-blob-util";
import { zip } from "react-native-zip-archive";
import { DatabaseLogger, db } from "../common/database/index";
import Storage from "../common/database/storage";
import { convertNoteToText } from "../utils/note-to-text";
import { sleep } from "../utils/time";
import { sanitizeFilename } from "@notesnook/common";
import { presentDialog } from "../components/dialog/functions";
import { useSettingStore } from "../stores/use-setting-store";
import BiometicService from "./biometrics";
import { ToastEvent } from "./event-manager";
const MIMETypes = {
txt: "text/plain",
@@ -99,8 +101,8 @@ async function save(path, data, fileName, extension) {
return uri || path;
}
async function makeHtml(note) {
let html = await db.notes.note(note.id).export("html");
async function makeHtml(note, content) {
let html = await db.notes.note(note.id).export("html", content);
html = decode(html, {
level: EntityLevel.HTML
});
@@ -109,22 +111,27 @@ async function makeHtml(note) {
/**
*
* @param {"txt" | "pdf" | "md" | "html"} type
* @param {"txt" | "pdf" | "md" | "html" | "md-frontmatter"} type
*/
async function exportAs(type, note, bulk) {
async function exportAs(type, note, bulk, content) {
let data;
switch (type) {
case "html":
{
data = await makeHtml(note);
data = await makeHtml(note, content);
}
break;
case "md":
data = await db.notes.note(note.id).export("md");
data = await db.notes.note(note.id).export("md", content);
break;
case "md-frontmatter":
data = await db.notes
.note(note.id)
.export("md-frontmatter", content?.data);
break;
case "pdf":
{
let html = await makeHtml(note);
let html = await makeHtml(note, content);
let fileName = sanitizeFilename(note.title + Date.now(), {
replacement: "_"
});
@@ -150,21 +157,84 @@ async function exportAs(type, note, bulk) {
}
break;
case "txt":
data = await convertNoteToText(note);
{
data = await db.notes?.note(note.id).export("txt", content);
}
break;
}
return data;
}
async function unlockVault() {
let biometry = await BiometicService.isBiometryAvailable();
let fingerprint = await BiometicService.hasInternetCredentials("nn_vault");
if (biometry && fingerprint) {
let credentials = await BiometicService.getCredentials(
"Unlock vault",
"Unlock vault to export locked notes"
);
if (credentials) {
return db.vault.unlock(credentials.password);
}
}
useSettingStore.getState().setSheetKeyboardHandler(false);
return new Promise((resolve) => {
setImmediate(() => {
presentDialog({
context: "export-notes",
input: true,
secureTextEntry: true,
positiveText: "Unlock",
title: "Unlock vault",
paragraph: "Some exported notes are locked, Unlock to export them",
inputPlaceholder: "Enter password",
positivePress: async (value) => {
const unlocked = await db.vault.unlock(value);
if (!unlocked) {
ToastEvent.show({
heading: "Invalid password",
message: "Please enter a valid password",
type: "error",
context: "local"
});
return false;
}
resolve(unlocked);
useSettingStore.getState().setSheetKeyboardHandler(true);
return true;
},
onClose: () => {
resolve(false);
useSettingStore.getState().setSheetKeyboardHandler(true);
}
});
});
});
}
/**
*
* @param {"txt" | "pdf" | "md" | "html"} type
* @param {"txt" | "pdf" | "md" | "html" | "md-frontmatter"} type
*/
async function exportNote(note, type) {
let content;
if (note.locked) {
try {
let unlocked = await unlockVault();
if (!unlocked) return null;
const unlockedNote = await db.vault.open(note.id);
content = unlockedNote.content;
} catch (e) {
DatabaseLogger.error(e);
}
}
let path = await getPath(FolderNames[type]);
if (!path) return;
let result = await exportAs(type, note);
let result = await exportAs(type, note, false, content);
if (!result) return null;
let fileName = sanitizeFilename(note.title + Date.now(), {
replacement: "_"
@@ -180,7 +250,7 @@ async function exportNote(note, type) {
filePath: path,
type: "text/plain",
name: "Text",
fileName: fileName + `.${type}`
fileName: fileName + `.${type === "md-frontmatter" ? "md" : type}`
};
}
@@ -192,11 +262,6 @@ function copyFileAsync(source, dest) {
});
}
function zipsync(results) {
let data = zipSync(results);
return Buffer.from(data.buffer).toString("base64");
}
function getUniqueFileName(fileName, results) {
const chunks = fileName.split(".");
const ext = chunks.pop();
@@ -212,25 +277,114 @@ function getUniqueFileName(fileName, results) {
/**
*
* @param {"txt" | "pdf" | "md" | "html"} type
* @param {"txt" | "pdf" | "md" | "html" | "md-frontmatter"} type
*/
async function bulkExport(notes, type, callback) {
let path = await getPath(FolderNames[type]);
if (!path) return;
const exportCacheFolder =
RNFetchBlob.fs.dirs.CacheDir + `/export_${Date.now()}`;
await RNFetchBlob.fs.mkdir(exportCacheFolder).catch((e) => console.log(e));
const mkdir = async (dir) => {
const folder = `${exportCacheFolder}/${dir}`;
if (!(await RNFetchBlob.fs.exists(folder))) {
await RNFetchBlob.fs.mkdir(folder);
}
};
const writeFile = async (path, result) => {
const cacheFilePath = exportCacheFolder + path;
await RNFetchBlob.fs.writeFile(
cacheFilePath,
result,
type === "pdf" ? "base64" : "utf8"
);
};
const results = {};
for (var i = 0; i < notes.length; i++) {
try {
await sleep(1);
let note = notes[i];
if (note.locked) continue;
let result = await exportAs(type, note);
let content;
if (note.locked) {
try {
let unlocked = !db.vault.unlocked ? await unlockVault() : true;
if (!unlocked) {
continue;
}
const unlockedNote = await db.vault.open(note.id);
content = unlockedNote.content;
} catch (e) {
DatabaseLogger.error(e);
continue;
}
}
let result = await exportAs(type, note, true, content);
let fileName = sanitizeFilename(note.title, {
replacement: "_"
});
if (result) {
results[getUniqueFileName(fileName + `.${type}`, results)] =
Buffer.from(result, type === "pdf" ? "base64" : "utf-8");
const notebooks = [
...(db.relations
?.to({ id: note.id, type: "note" }, "notebook")
.map((notebook) => ({
title: notebook.title
})) || []),
...(note.notebooks || []).map((ref) => {
const notebook = db.notebooks?.notebook(ref.id);
const topics = notebook?.topics.all || [];
return {
title: notebook?.title,
topics: ref.topics
.map((topicId) => topics.find((topic) => topic.id === topicId))
.filter(Boolean)
};
})
];
for (const notebook of notebooks) {
results[notebook.title] = results[notebook.title] || {};
await mkdir(notebook.title);
if (notebook.topics && notebook.topics.length) {
for (const topic of notebook.topics) {
results[notebook.title][topic.title] =
results[notebook.title][topic.title] || {};
await mkdir(`${notebook.title}/${topic.title}`);
const exportedNoteName = getUniqueFileName(
fileName + `.${type}`,
results[notebook.title][topic.title]
);
results[notebook.title][topic.title][exportedNoteName] = true;
writeFile(
`/${notebook.title}/${topic.title}/${exportedNoteName}`,
result
);
}
} else {
const exportedNoteName = getUniqueFileName(
fileName + `.${type}`,
results[notebook.title]
);
results[notebook.title][exportedNoteName] = true;
writeFile(`/${notebook.title}/${exportedNoteName}`, result);
}
}
if (!notebooks.length) {
const exportedNoteName = getUniqueFileName(
fileName + `.${type}`,
results
);
results[exportedNoteName] = true;
writeFile(`/${exportedNoteName}`, result);
}
}
callback(`${i + 1}/${notes.length}`);
} catch (e) {
@@ -241,26 +395,24 @@ async function bulkExport(notes, type, callback) {
try {
callback("zipping");
await sleep(1);
const zipped = zipsync(results);
const zipOutputPath =
Platform.OS === "ios"
? path + `/${fileName}`
: RNFetchBlob.fs.dirs.CacheDir + `/${fileName}`;
await zip(exportCacheFolder, zipOutputPath);
callback("saving to disk");
await sleep(1);
if (Platform.OS === "ios") {
RNFetchBlob.fs.writeFile(path + `/${fileName}`, zipped, "base64");
} else {
const templFile = RNFetchBlob.fs.dirs.CacheDir + `/${fileName}`;
await RNFetchBlob.fs.writeFile(templFile, zipped, "base64");
if (Platform.OS === "android") {
const file = await ScopedStorage.createFile(
path,
fileName,
"application/zip"
);
path = file.uri;
await copyFileAsync("file://" + templFile, path);
await RNFetchBlob.fs.unlink(templFile);
await copyFileAsync("file://" + zipOutputPath, path);
await RNFetchBlob.fs.unlink(zipOutputPath);
callback();
}
RNFetchBlob.fs.unlink(exportCacheFolder);
} catch (e) {
DatabaseLogger.error(e);
}

View File

@@ -39,6 +39,10 @@ import { eSendEvent, presentSheet, ToastEvent } from "./event-manager";
import SettingsService from "./settings";
let premiumStatus = 0;
/**
* @type {RNIap.Subscription[]}
*/
let products = [];
let user = null;
@@ -68,7 +72,9 @@ async function setPremiumStatus() {
}
try {
await RNIap.initConnection();
products = await RNIap.getSubscriptions(itemSkus);
products = await RNIap.getSubscriptions({
skus: itemSkus
});
} catch (e) {
console.log("subscriptions: ", e);
}
@@ -223,12 +229,21 @@ const showVerifyEmailDialog = () => {
};
const subscriptions = {
get: async () => {
/**
*
* @returns {RNIap.Purchase} subscription
*/
get: () => {
if (Platform.OS === "android") return;
let _subscriptions = MMKV.getString("subscriptionsIOS");
if (!_subscriptions) return [];
return JSON.parse(_subscriptions);
},
/**
*
* @param {RNIap.Purchase} subscription
* @returns
*/
set: async (subscription) => {
if (Platform.OS === "android") return;
let _subscriptions = MMKV.getString("subscriptionsIOS");
@@ -263,6 +278,10 @@ const subscriptions = {
MMKV.setString("subscriptionsIOS", JSON.stringify(_subscriptions));
}
},
/**
*
* @param {RNIap.Purchase} subscription
*/
verify: async (subscription) => {
if (Platform.OS === "android") return;
@@ -280,12 +299,16 @@ const subscriptions = {
"Content-Type": "application/json"
}
};
console.log("Subscription.verify", requestData);
try {
let result = await fetch(
"https://payments.streetwriters.co/apple/verify",
__DEV__
? "http://192.168.43.5:4264/apple/verify"
: "https://payments.streetwriters.co/apple/verify",
requestData
);
console.log("Subscribed", result);
let text = await result.text();
if (!result.ok) {
@@ -293,6 +316,8 @@ const subscriptions = {
await subscriptions.clear(subscription);
}
return;
} else {
await subscriptions.clear(subscription);
}
} catch (e) {
console.log("subscription error", e);
@@ -302,15 +327,18 @@ const subscriptions = {
},
clear: async (_subscription) => {
if (Platform.OS === "android") return;
let _subscriptions = await subscriptions.get();
let _subscriptions = subscriptions.get();
let subscription = null;
if (_subscription) {
subscription = _subscription;
} else {
subscription = _subscriptions.length > 0 ? _subscriptions[0] : null;
}
if (subscription) {
await RNIap.finishTransaction(subscription.transactionId);
await RNIap.finishTransaction({
purchase: subscription
});
await RNIap.clearTransactionIOS();
await subscriptions.remove(subscription.transactionId);
}

View File

@@ -68,8 +68,7 @@ export const useAttachmentStore = create<AttachmentStore>((set, get) => ({
if (!progress) return;
editorController.current?.commands.setAttachmentProgress({
hash: hash,
progress: 100,
type: progress[hash]?.type || "download"
progress: 100
});
progress[hash] = null;
set({ progress: { ...progress } });
@@ -80,10 +79,11 @@ export const useAttachmentStore = create<AttachmentStore>((set, get) => ({
progress[hash] = { sent, total, hash, recieved, type };
const progressPercentage =
type === "upload" ? sent / total : recieved / total;
editorController.current?.commands.setAttachmentProgress({
hash: hash,
progress: Math.round(Math.max(progressPercentage * 100, 0)),
type: type
//@ts-ignore
progress: Math.round(Math.max(progressPercentage * 100, 0))
});
set({ progress: { ...progress } });
},

View File

@@ -75,7 +75,7 @@ export const SUBSCRIPTION_STATUS_STRINGS = {
2: Platform.OS === "ios" ? "Pro" : "Beta",
5: "Pro",
6: "Expired",
7: "Pro"
7: "Pro (cancelled)"
};
export const SUBSCRIPTION_PROVIDER = {

View File

@@ -67,15 +67,10 @@ export const deleteItems = async (item, context) => {
});
return;
}
if (
item &&
item.id &&
useSelectionStore.getState().selectedItemsList.length === 0
) {
useSelectionStore.getState().setSelectedItem(item);
}
const { selectedItemsList } = useSelectionStore.getState();
const selectedItemsList = item
? [item]
: useSelectionStore.getState().selectedItemsList;
let notes = selectedItemsList.filter((i) => i.type === "note");
let notebooks = selectedItemsList.filter((i) => i.type === "notebook");
@@ -179,7 +174,9 @@ export const deleteItems = async (item, context) => {
});
}
Navigation.queueRoutesForUpdate();
useSelectionStore.getState().clearSelection();
if (!item) {
useSelectionStore.getState().clearSelection();
}
useMenuStore.getState().setMenuPins();
useMenuStore.getState().setColorNotes();
SearchService.updateAndSearch();

View File

@@ -21,7 +21,7 @@ import Sodium from "@ammarahmed/react-native-sodium";
import { isImage } from "@notesnook/core/dist/utils/filename";
import { Platform } from "react-native";
import RNFetchBlob from "react-native-blob-util";
import { db } from "../common/database";
import { DatabaseLogger, db } from "../common/database";
import { IOS_APPGROUPID } from "./constants";
const santizeUri = (uri) => {
@@ -57,6 +57,7 @@ export async function attachFile(uri, hash, type, filename, options) {
return true;
} catch (e) {
if (Platform.OS === "ios") RNFetchBlob.fs.unlink(uri).catch(console.log);
DatabaseLogger.error(e, "Attach file error");
console.log("attach file error: ", e);
return false;
}
@@ -107,26 +108,31 @@ async function createNotes(bundle) {
uri: uri,
type: "cache"
});
await attachFile(uri, hash, file.type, file.name, {
const attached = await attachFile(uri, hash, file.type, file.name, {
type: "cache",
id: id,
appGroupId: IOS_APPGROUPID
});
let content = ``;
if (isImage(file.type)) {
content = `<img data-hash="${hash}" data-mime="${file.type}" data-filename="${file.name}" />`;
} else {
content = `<p><span data-hash="${hash}" data-mime="${file.type}" data-filename="${file.name}" data-size="${file.size}" /></p>`;
if (attached) {
if (isImage(file.type)) {
content = `<img data-hash="${hash}" data-mime="${file.type}" data-filename="${file.name}" />`;
} else {
content = `<p><span data-hash="${hash}" data-mime="${file.type}" data-filename="${file.name}" data-size="${file.size}" /></p>`;
}
const rawContent = await db.content.raw(
db.notes.note(id).data?.contentId
);
await db.notes.add({
id: id,
content: {
type: "tiptap",
data: rawContent?.data ? rawContent?.data + content : content
},
sessionId: sessionId
});
}
const rawContent = await db.content.raw(db.notes.note(id).data?.contentId);
await db.notes.add({
id: id,
content: {
type: "tiptap",
data: rawContent?.data ? rawContent?.data + content : content
},
sessionId: sessionId
});
}
}

View File

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

View File

@@ -8,6 +8,10 @@ import com.ammarahmed.mmkv.MMKV;
public class OnClearFromRecentService extends Service {
static {
System.loadLibrary("rnmmkv");
}
@Override
public IBinder onBind(Intent intent) {
return null;
@@ -15,7 +19,6 @@ public class OnClearFromRecentService extends Service {
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
return START_NOT_STICKY;
}
@@ -31,8 +34,10 @@ public class OnClearFromRecentService extends Service {
MMKV mmkv = MMKV.mmkvWithID("default",MMKV.SINGLE_PROCESS_MODE);
mmkv.removeValueForKey("appState");
stopSelf();
} catch (Exception e) {
} catch (UnsatisfiedLinkError e) {
} catch (Exception e) {
}
//System.exit(0);
}

View File

@@ -5,7 +5,7 @@
<!-- Customize your theme here. -->
<item name="android:windowDisablePreview">true</item>
<item name="android:editTextBackground">@drawable/edit_text</item>
<item name="android:navigationBarColor">@android:color/transparent</item>
<item name="android:statusBarColor">@android:color/transparent</item>
<item name="android:datePickerDialogTheme">@style/DialogDatePicker.Theme</item>
</style>
@@ -20,6 +20,7 @@
<style name="BootTheme" parent="Theme.SplashScreen">
<item name="windowSplashScreenBackground">@color/bootsplash_background</item>
<item name="windowSplashScreenAnimatedIcon">@mipmap/bootsplash_logo_dark</item>
<item name="android:navigationBarColor">@color/bootsplash_background</item>
<item name="postSplashScreenTheme">@style/AppTheme</item>
</style>
@@ -31,6 +32,7 @@
<item name="android:windowIsFloating">true</item>
<item name="android:backgroundDimEnabled">true</item>
<item name="android:editTextBackground">@drawable/edit_text</item>
<item name="android:navigationBarColor">@android:color/transparent</item>
</style>
<style name="DialogDatePicker.Theme" parent="Theme.AppCompat.Light.Dialog">

View File

@@ -5,7 +5,7 @@
<!-- Customize your theme here. -->
<item name="android:windowDisablePreview">true</item>
<item name="android:editTextBackground">@drawable/edit_text</item>
<item name="android:navigationBarColor">@android:color/transparent</item>
<item name="android:statusBarColor">@android:color/transparent</item>
<item name="android:datePickerDialogTheme">@style/DialogDatePicker.Theme</item>
</style>
@@ -20,6 +20,7 @@
<style name="BootTheme" parent="Theme.SplashScreen">
<item name="windowSplashScreenBackground">@color/bootsplash_background</item>
<item name="windowSplashScreenAnimatedIcon">@mipmap/bootsplash_logo</item>
<item name="android:navigationBarColor">@color/bootsplash_background</item>
<item name="postSplashScreenTheme">@style/AppTheme</item>
</style>
@@ -31,6 +32,7 @@
<item name="android:windowIsFloating">true</item>
<item name="android:backgroundDimEnabled">true</item>
<item name="android:editTextBackground">@drawable/edit_text</item>
<item name="android:navigationBarColor">@android:color/transparent</item>
</style>
<style name="DialogDatePicker.Theme" parent="Theme.AppCompat.Light.Dialog">

View File

@@ -7,7 +7,7 @@ buildscript {
minSdkVersion = 21
compileSdkVersion = 33
targetSdkVersion = 33
ext.kotlinVersion = '1.6.10'
ext.kotlinVersion = '1.8.0'
androidXAnnotation = "1.1.0"
androidXCoreVersion = "1.7.0"
androidXCore = "1.7.0"

View File

@@ -1,3 +1,10 @@
- Bug fixes and performance improvements
- Support for exporting locked notes
- Maintain directory structure in exports based on Notebooks/Topics
- Match color of navigation bar in sheets with app theme
- Improved image loading and show progress when loading images
- Support for setting trash interval to daily
- Fix android app UI issues with RTL languages
- Fix pasting links in editor
- Other Bug fixes and performance improvements
Thank you for using Notesnook!

View File

@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>provisioningProfiles</key>
<dict>
<key>org.streetwriters.notesnook</key>
<string>Notesnook App Distribution 2024</string>
<key>org.streetwriters.notesnook.notewidget</key>
<string>Notesnook Widget Distribution 2024</string>
<key>org.streetwriters.notesnook.share</key>
<string>Notesnook Share Distribution 2024</string>
</dict>
</dict>
</plist>

View File

@@ -14,13 +14,13 @@
2D02E4BD1E0B4A84006451C7 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; };
2D02E4BF1E0B4AB3006451C7 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; };
2DCD954D1E0B4F2C00145EB5 /* NotesnookTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 00E356F21AD99517003FC87E /* NotesnookTests.m */; };
6510626F27042891009661C3 /* OpenSans-Regular.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 6552012F27019F7700A43C51 /* OpenSans-Regular.ttf */; };
6510627627042895009661C3 /* OpenSans-SemiBold.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 6552012E27019F6E00A43C51 /* OpenSans-SemiBold.ttf */; };
6510E6D72877215700DACAA9 /* build.bundle in Resources */ = {isa = PBXBuildFile; fileRef = 6510E6D62877215700DACAA9 /* build.bundle */; };
6515C42F2580AA3000E83E39 /* StoreKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6515C42E2580AA2F00E83E39 /* StoreKit.framework */; };
6517B7C12B6838EB0079FF37 /* OpenSans-Regular.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 6517B7BE2B6838EB0079FF37 /* OpenSans-Regular.ttf */; };
6517B7C22B6838EB0079FF37 /* OpenSans-SemiBold.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 6517B7BF2B6838EB0079FF37 /* OpenSans-SemiBold.ttf */; };
6517B7C32B6838EB0079FF37 /* OpenSans-Bold.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 6517B7C02B6838EB0079FF37 /* OpenSans-Bold.ttf */; };
6529A13E279BC4C70048D4A8 /* BootSplash.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 6529A13D279BC4C70048D4A8 /* BootSplash.storyboard */; };
6593E4A3281C345400492C50 /* AppDelegate.mm in Sources */ = {isa = PBXBuildFile; fileRef = 6593E4A2281C345400492C50 /* AppDelegate.mm */; };
659670B22A2754FD00C5D2AF /* OpenSans-Bold.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 659670B12A2754FD00C5D2AF /* OpenSans-Bold.ttf */; };
659BE46725E11A5100E05671 /* notesnook-text.png in Resources */ = {isa = PBXBuildFile; fileRef = 659BE46625E11A5100E05671 /* notesnook-text.png */; };
65AA857925E6DDEC00772A01 /* WidgetKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 65AA857825E6DDEC00772A01 /* WidgetKit.framework */; };
65AA857B25E6DDEC00772A01 /* SwiftUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 65AA857A25E6DDEC00772A01 /* SwiftUI.framework */; };
@@ -110,12 +110,12 @@
4C4AF691C324D95337F2FB0A /* libPods-Make Note.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Make Note.a"; sourceTree = BUILT_PRODUCTS_DIR; };
6510E6D62877215700DACAA9 /* build.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; name = build.bundle; path = "../../../../packages/editor-mobile/build.bundle"; sourceTree = "<group>"; };
6515C42E2580AA2F00E83E39 /* StoreKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = StoreKit.framework; path = System/Library/Frameworks/StoreKit.framework; sourceTree = SDKROOT; };
6517B7BE2B6838EB0079FF37 /* OpenSans-Regular.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; name = "OpenSans-Regular.ttf"; path = "../../../../packages/editor-mobile/public/fonts/OpenSans-Regular.ttf"; sourceTree = "<group>"; };
6517B7BF2B6838EB0079FF37 /* OpenSans-SemiBold.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; name = "OpenSans-SemiBold.ttf"; path = "../../../../packages/editor-mobile/public/fonts/OpenSans-SemiBold.ttf"; sourceTree = "<group>"; };
6517B7C02B6838EB0079FF37 /* OpenSans-Bold.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; name = "OpenSans-Bold.ttf"; path = "../../../../packages/editor-mobile/public/fonts/OpenSans-Bold.ttf"; sourceTree = "<group>"; };
6529A13D279BC4C70048D4A8 /* BootSplash.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = BootSplash.storyboard; path = Notesnook/BootSplash.storyboard; sourceTree = "<group>"; };
6529D2B0257B4A2900B49BC3 /* NotesnookDebug.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; name = NotesnookDebug.entitlements; path = Notesnook/NotesnookDebug.entitlements; sourceTree = "<group>"; };
6552012E27019F6E00A43C51 /* OpenSans-SemiBold.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; name = "OpenSans-SemiBold.ttf"; path = "../android/app/src/main/assets/fonts/OpenSans-SemiBold.ttf"; sourceTree = "<group>"; };
6552012F27019F7700A43C51 /* OpenSans-Regular.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; name = "OpenSans-Regular.ttf"; path = "../android/app/src/main/assets/fonts/OpenSans-Regular.ttf"; sourceTree = "<group>"; };
6593E4A2281C345400492C50 /* AppDelegate.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = AppDelegate.mm; path = Notesnook/AppDelegate.mm; sourceTree = "<group>"; };
659670B12A2754FD00C5D2AF /* OpenSans-Bold.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; name = "OpenSans-Bold.ttf"; path = "../android/app/src/main/assets/fonts/OpenSans-Bold.ttf"; sourceTree = "<group>"; };
659BE46625E11A5100E05671 /* notesnook-text.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "notesnook-text.png"; path = "Notesnook/Images.xcassets/notesnook-text.png"; sourceTree = "<group>"; };
65A7F34F255689AD00699170 /* Notesnook.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; name = Notesnook.entitlements; path = Notesnook/Notesnook.entitlements; sourceTree = "<group>"; };
65AA857725E6DDEC00772A01 /* NotesWidgetExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = NotesWidgetExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; };
@@ -293,13 +293,13 @@
83CBB9F61A601CBA00E9B192 = {
isa = PBXGroup;
children = (
6517B7C02B6838EB0079FF37 /* OpenSans-Bold.ttf */,
6517B7BE2B6838EB0079FF37 /* OpenSans-Regular.ttf */,
6517B7BF2B6838EB0079FF37 /* OpenSans-SemiBold.ttf */,
65C400DD2A80B68D00AA3DF5 /* MaterialCommunityIcons.ttf */,
65C149862A61151B005C40F1 /* extension.bundle */,
6510E6D62877215700DACAA9 /* build.bundle */,
65EC5B71272A7EE200FB3748 /* NotesWidgetExtensionDebug.entitlements */,
6552012F27019F7700A43C51 /* OpenSans-Regular.ttf */,
659670B12A2754FD00C5D2AF /* OpenSans-Bold.ttf */,
6552012E27019F6E00A43C51 /* OpenSans-SemiBold.ttf */,
65D145C429DC30470056FE7D /* MaterialCommunityIcons.ttf */,
13B07FAE1A68108700A75B9A /* Notesnook */,
832341AE1AAA6A7D00B99B32 /* Libraries */,
@@ -542,12 +542,12 @@
buildActionMask = 2147483647;
files = (
65C400DE2A80B68D00AA3DF5 /* MaterialCommunityIcons.ttf in Resources */,
6510626F27042891009661C3 /* OpenSans-Regular.ttf in Resources */,
659670B22A2754FD00C5D2AF /* OpenSans-Bold.ttf in Resources */,
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */,
6517B7C32B6838EB0079FF37 /* OpenSans-Bold.ttf in Resources */,
6517B7C22B6838EB0079FF37 /* OpenSans-SemiBold.ttf in Resources */,
6529A13E279BC4C70048D4A8 /* BootSplash.storyboard in Resources */,
6510E6D72877215700DACAA9 /* build.bundle in Resources */,
6510627627042895009661C3 /* OpenSans-SemiBold.ttf in Resources */,
6517B7C12B6838EB0079FF37 /* OpenSans-Regular.ttf in Resources */,
659BE46725E11A5100E05671 /* notesnook-text.png in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
@@ -997,7 +997,7 @@
CODE_SIGN_IDENTITY = "Apple Development";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 2058;
CURRENT_PROJECT_VERSION = 2067;
DEVELOPMENT_TEAM = 53CWBG3QUC;
ENABLE_BITCODE = NO;
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
@@ -1071,7 +1071,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 2.6.7;
MARKETING_VERSION = 2.6.15;
OTHER_LDFLAGS = (
"$(inherited)",
"-ObjC",
@@ -1102,7 +1102,7 @@
CODE_SIGN_IDENTITY = "Apple Development";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
CODE_SIGN_STYLE = Manual;
CURRENT_PROJECT_VERSION = 2058;
CURRENT_PROJECT_VERSION = 2067;
DEVELOPMENT_TEAM = "";
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = 53CWBG3QUC;
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
@@ -1176,7 +1176,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 2.6.7;
MARKETING_VERSION = 2.6.15;
ONLY_ACTIVE_ARCH = NO;
OTHER_LDFLAGS = (
"$(inherited)",
@@ -1186,7 +1186,7 @@
PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook;
PRODUCT_NAME = Notesnook;
PROVISIONING_PROFILE_SPECIFIER = "";
"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "Notesnook Distribution 2023";
"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "Notesnook App Distribution 2024";
SWIFT_OBJC_BRIDGING_HEADER = "Notesnook-Bridging-Header.h";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
@@ -1335,7 +1335,7 @@
CODE_SIGN_IDENTITY = "Apple Development";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 2058;
CURRENT_PROJECT_VERSION = 2067;
DEBUG_INFORMATION_FORMAT = dwarf;
DEVELOPMENT_TEAM = 53CWBG3QUC;
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
@@ -1347,7 +1347,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 2.6.7;
MARKETING_VERSION = 2.6.15;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook.notewidget;
@@ -1378,7 +1378,7 @@
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
CODE_SIGN_STYLE = Manual;
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 2058;
CURRENT_PROJECT_VERSION = 2067;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEVELOPMENT_TEAM = "";
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = 53CWBG3QUC;
@@ -1391,12 +1391,12 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 2.6.7;
MARKETING_VERSION = 2.6.15;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook.notewidget;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "Notesnook Widget Distribution 2023";
"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "Notesnook Widget Distribution 2024";
SKIP_INSTALL = YES;
SWIFT_COMPILATION_MODE = wholemodule;
SWIFT_OPTIMIZATION_LEVEL = "-O";
@@ -1421,7 +1421,7 @@
CODE_SIGN_ENTITLEMENTS = "Make Note/Make Note.entitlements";
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 2058;
CURRENT_PROJECT_VERSION = 2067;
DEBUG_INFORMATION_FORMAT = dwarf;
DEVELOPMENT_TEAM = 53CWBG3QUC;
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
@@ -1495,7 +1495,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 2.6.7;
MARKETING_VERSION = 2.6.15;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook.share;
@@ -1526,7 +1526,7 @@
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
CODE_SIGN_STYLE = Manual;
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 2058;
CURRENT_PROJECT_VERSION = 2067;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEVELOPMENT_TEAM = "";
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = 53CWBG3QUC;
@@ -1601,12 +1601,12 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 2.6.7;
MARKETING_VERSION = 2.6.15;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook.share;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "Notesnook Share Distrubution 2023";
"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "Notesnook Share Distribution 2024";
SKIP_INSTALL = YES;
SWIFT_OBJC_BRIDGING_HEADER = "Make Note/Make Note-Bridging-Header.h";
SWIFT_VERSION = 5.0;

View File

@@ -327,7 +327,7 @@ PODS:
- React-Core
- react-native-keep-awake (1.2.0):
- React-Core
- react-native-mmkv-storage (0.10.0-alpha.9):
- react-native-mmkv-storage (0.10.0-alpha.11):
- MMKV (~> 1.3.1)
- React
- React-Core
@@ -477,7 +477,7 @@ PODS:
- React-Core
- RNGestureHandler (2.12.0):
- React-Core
- RNIap (7.5.6):
- RNIap (12.11.0):
- React-Core
- RNKeychain (4.0.5):
- React
@@ -875,7 +875,7 @@ SPEC CHECKSUMS:
react-native-image-resizer: 00ceb0e05586c7aadf061eea676957a6c2ec60fa
react-native-in-app-review: db8bb167a5f238e7ceca5c242d6b36ce8c4404a4
react-native-keep-awake: caee3ff89eaa21dfe29010f0d143566874a04441
react-native-mmkv-storage: d4ad55ab411b7f0d4e6269d801b31821ef48970b
react-native-mmkv-storage: e798639f91601896f1c84f4eb3d6f19af2f4a160
react-native-netinfo: ccbe1085dffd16592791d550189772e13bf479e2
react-native-notification-sounds: da78c828fe1bcbb92d8b505d5261890ed315ff39
react-native-orientation: f1caf84d65f1a4fd4511a18f2b924e634ad7a628
@@ -911,7 +911,7 @@ SPEC CHECKSUMS:
RNFileViewer: ce7ca3ac370e18554d35d6355cffd7c30437c592
RNFlashList: 399bf6a0db68f594ad2c86aaff3ea39564f39f8a
RNGestureHandler: dec4645026e7401a0899f2846d864403478ff6a5
RNIap: d248609d1b8937e63bd904e865c318e9b1457eff
RNIap: fc9af04ee706894a80c9d8f979bae930b0dee191
RNKeychain: 840f8e6f13be0576202aefcdffd26a4f54bfe7b5
RNNotifee: 2ae3c18196e6f307fa62ae5c8e5305dea03ff147
RNPrivacySnapshot: 8eaf571478a353f2e5184f5c803164f22428b023
@@ -931,4 +931,4 @@ SPEC CHECKSUMS:
PODFILE CHECKSUM: b0cadd188aa428b98d1eaa0b84b3b6208e714119
COCOAPODS: 1.12.1
COCOAPODS: 1.14.2

File diff suppressed because one or more lines are too long

View File

@@ -40,11 +40,11 @@
"react-native-get-random-values": "^1.7.0",
"react-native-gzip": "1.1.0",
"react-native-html-to-pdf-lite": "^0.9.1",
"react-native-iap": "7.5.6",
"react-native-iap": "12.11.0",
"react-native-image-picker": "4.1.2",
"react-native-in-app-review": "4.3.3",
"react-native-keychain": "4.0.5",
"react-native-mmkv-storage": "^0.10.0-alpha.9",
"react-native-mmkv-storage": "^0.10.0-alpha.11",
"react-native-modal-datetime-picker": "14.0.0",
"react-native-navigation-bar-color": "2.0.2",
"react-native-notification-sounds": "0.5.5",

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{
"name": "@notesnook/mobile",
"version": "2.6.7",
"version": "2.6.15",
"private": true,
"license": "GPL-3.0-or-later",
"workspaces": [
@@ -10,6 +10,7 @@
"scripts": {
"postinstall": "patch-package && node ./scripts/optimize-fonts.mjs",
"run-android": "cd native && react-native run-android --active-arch-only",
"build": "echo 'No build script defined'",
"run-ios": "cd native && react-native run-ios",
"start": "cd native && react-native start",
"repack": "cd native && react-native webpack-start",
@@ -41,6 +42,7 @@
"@trpc/react-query": "10.38.3",
"@trpc/server": "10.38.3",
"react": "18.2.0",
"react-native": "0.72.0"
"react-native": "0.72.0",
"react-native-iap": "12.11.0"
}
}
}

View File

@@ -15,7 +15,9 @@
"@notesnook/theme": ["../../packages/theme"],
"@types/react": ["./node_modules/@types/react"]
},
"incremental": true
"incremental": true,
"maxNodeModuleJsDepth": 5,
"downlevelIteration": true
},
"exclude": ["native"]
}

View File

@@ -1377,7 +1377,7 @@
},
"../web": {
"name": "@notesnook/web",
"version": "2.6.5",
"version": "2.6.14",
"license": "GPL-3.0-or-later",
"dependencies": {
"@aws-sdk/util-base64-browser": "^3.208.0",
@@ -1388,7 +1388,7 @@
"@emotion/react": "11.11.1",
"@mdi/js": "^7.2.96",
"@mdi/react": "^1.6.1",
"@notesnook-importer/core": "^1.7.1",
"@notesnook-importer/core": "^2.0.0",
"@notesnook/common": "file:../../packages/common",
"@notesnook/core": "file:../../packages/core",
"@notesnook/crypto": "file:../../packages/crypto",
@@ -1408,6 +1408,7 @@
"@theme-ui/core": "^0.14.7",
"@trpc/client": "10.38.3",
"@trpc/react-query": "10.38.3",
"@zip.js/zip.js": "^2.7.32",
"allotment": "^1.19.0",
"axios": "^1.3.4",
"clipboard-polyfill": "4.0.0",
@@ -1446,6 +1447,7 @@
"devDependencies": {
"@babel/core": "^7.22.5",
"@playwright/test": "^1.36.2",
"@swc/core": "1.3.61",
"@trpc/server": "10.38.3",
"@types/babel__core": "^7.20.1",
"@types/file-saver": "^2.0.5",
@@ -1456,7 +1458,8 @@
"@types/react-dom": "17.0.2",
"@types/react-modal": "3.13.1",
"@types/tinycolor2": "^1.4.3",
"@vitejs/plugin-react-swc": "^3.3.2",
"@types/wicg-file-system-access": "^2020.9.6",
"@vitejs/plugin-react-swc": "3.3.2",
"autoprefixer": "^10.4.14",
"buffer": "^6.0.3",
"chalk": "^4.1.0",
@@ -1470,14 +1473,14 @@
"otplib": "^12.0.1",
"react": "17.0.2",
"react-dom": "17.0.2",
"rollup": "^3.27.0",
"rollup": "^3.29.4",
"rollup-plugin-visualizer": "^5.9.2",
"swc-plugin-react-remove-properties": "^0.1.4",
"vite": "^4.3.9",
"vite": "^4.5.0",
"vite-plugin-env-compatible": "^1.1.1",
"vite-plugin-pwa": "^0.16.3",
"vite-plugin-svgr": "^3.2.0",
"vitest": "^0.32.0",
"vitest": "^0.34.6",
"workbox-core": "^7.0.0",
"workbox-expiration": "^7.0.0",
"workbox-precaching": "^7.0.0",
@@ -2406,15 +2409,6 @@
"@trpc/server": "10.38.3"
}
},
"node_modules/@trpc/server": {
"version": "10.38.3",
"resolved": "https://registry.npmjs.org/@trpc/server/-/server-10.38.3.tgz",
"integrity": "sha512-9s8/kwo2IDB5hwB2SKZZrfevRhdb1f9fdXtIYd3lbQuf2jQaC/LyQuHaIQjDQoUx9updBfsHXcFFPiCP1DL6pg==",
"funding": [
"https://trpc.io/sponsor"
],
"peer": true
},
"node_modules/@types/babel__core": {
"version": "7.20.1",
"dev": true,

View File

@@ -171,7 +171,7 @@ export class EditorModel {
await this.tagInput.fill(tag);
await this.tagInput.press("Enter");
await this.tags
.locator(":scope", { hasText: new RegExp(`^${tag}$`) })
.locator("span", { hasText: new RegExp(`^${tag}$`) })
.waitFor();
}
}
@@ -181,7 +181,7 @@ export class EditorModel {
const count = await this.tags.count();
for (let i = 0; i < count; ++i) {
const item = this.tags.nth(i);
const tag = await item.textContent();
const tag = await item.locator("span").textContent();
if (tag) tags.push(tag);
}
return tags;

View File

@@ -8,8 +8,11 @@
content="This is Test 1Test 1Test 1Test 1Test 1Test 1Test 1Test 1Test 1Test 1"
/>
<title>Test 1 - Notesnook</title>
<meta name="created-on" content="xxx" />
<meta name="last-edited-on" content="xxx" />
<meta name="created-at" content="22-12-2023 11:33 AM" />
<meta name="updated-at" content="22-12-2023 11:33 AM" />
<link rel="stylesheet" href="https://app.notesnook.com/assets/editor-styles.css?d=0">
@@ -25,6 +28,11 @@
color: #202124;
font-family: "Open Sans", "Noto Sans", Frutiger, Calibri, Myriad, Arial, Ubuntu, Helvetica, -apple-system, BlinkMacSystemFont, sans-serif;
}
.math-block {
padding-top: 20px;
padding-bottom: 20px;
}
h1,
h2,

View File

@@ -2,6 +2,3 @@
This is Test 1Test 1Test 1Test 1Test 1Test 1Test 1Test 1Test 1Test 1
---

40353
apps/web/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,7 @@
{
"name": "@notesnook/web",
"description": "Your private note taking space",
"version": "2.6.7",
"version": "2.6.15",
"private": true,
"main": "./src/app.js",
"homepage": "https://notesnook.com/",
@@ -16,7 +16,7 @@
"@emotion/react": "11.11.1",
"@mdi/js": "^7.2.96",
"@mdi/react": "^1.6.1",
"@notesnook-importer/core": "^1.7.1",
"@notesnook-importer/core": "^2.0.0",
"@notesnook/common": "file:../../packages/common",
"@notesnook/core": "file:../../packages/core",
"@notesnook/crypto": "file:../../packages/crypto",
@@ -36,6 +36,7 @@
"@theme-ui/core": "^0.14.7",
"@trpc/client": "10.38.3",
"@trpc/react-query": "10.38.3",
"@zip.js/zip.js": "^2.7.32",
"allotment": "^1.19.0",
"axios": "^1.3.4",
"clipboard-polyfill": "4.0.0",
@@ -74,6 +75,7 @@
"devDependencies": {
"@babel/core": "^7.22.5",
"@playwright/test": "^1.36.2",
"@swc/core": "1.3.61",
"@trpc/server": "10.38.3",
"@types/babel__core": "^7.20.1",
"@types/file-saver": "^2.0.5",
@@ -84,7 +86,8 @@
"@types/react-dom": "17.0.2",
"@types/react-modal": "3.13.1",
"@types/tinycolor2": "^1.4.3",
"@vitejs/plugin-react-swc": "^3.3.2",
"@types/wicg-file-system-access": "^2020.9.6",
"@vitejs/plugin-react-swc": "3.3.2",
"autoprefixer": "^10.4.14",
"buffer": "^6.0.3",
"chalk": "^4.1.0",
@@ -98,14 +101,14 @@
"otplib": "^12.0.1",
"react": "17.0.2",
"react-dom": "17.0.2",
"rollup": "^3.27.0",
"rollup": "^3.29.4",
"rollup-plugin-visualizer": "^5.9.2",
"swc-plugin-react-remove-properties": "^0.1.4",
"vite": "^4.3.9",
"vite": "^4.5.0",
"vite-plugin-env-compatible": "^1.1.1",
"vite-plugin-pwa": "^0.16.3",
"vite-plugin-svgr": "^3.2.0",
"vitest": "^0.32.0",
"vitest": "^0.34.6",
"workbox-core": "^7.0.0",
"workbox-expiration": "^7.0.0",
"workbox-precaching": "^7.0.0",

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB

After

Width:  |  Height:  |  Size: 9.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

After

Width:  |  Height:  |  Size: 9.2 KiB

View File

@@ -129,7 +129,6 @@ export default function AppEffects({ setShow }: AppEffectsProps) {
) {
const [key, status] = getProcessingStatusFromType(type);
console.log("handleDownloadUploadProgresss", key, status, current, total);
if (current === total) {
removeStatus(key);
} else {

View File

@@ -24,6 +24,7 @@ import Config from "./utils/config";
import { initalizeLogger, logger } from "./utils/logger";
import { AuthProps } from "./views/auth";
import { initializeFeatureChecks } from "./utils/feature-check";
type Route<TProps = null> = {
component: () => Promise<{
@@ -142,6 +143,8 @@ function isSessionExpired(path: Routes): RouteWithPath<AuthProps> | null {
export async function init() {
await initalizeLogger();
await initializeFeatureChecks();
const { path, route } = getRoute();
return { ...route, path };
}

View File

@@ -17,14 +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 FS from "../interfaces/fs";
import { lazify } from "../utils/lazify";
import { db } from "./db";
async function download(hash: string) {
async function download(hash: string, groupId?: string) {
const attachment = db.attachments?.attachment(hash);
if (!attachment) return;
const downloadResult = await db.fs?.downloadFile(
attachment.metadata.hash,
groupId || attachment.metadata.hash,
attachment.metadata.hash,
attachment.chunkSize,
attachment.metadata
@@ -42,13 +42,15 @@ export async function saveAttachment(hash: string) {
if (!response) return;
const { attachment, key } = response;
await FS.saveFile(attachment.metadata.hash, {
key,
iv: attachment.iv,
name: attachment.metadata.filename,
type: attachment.metadata.type,
isUploaded: !!attachment.dateUploaded
});
await lazify(import("../interfaces/fs"), ({ default: FS }) =>
FS.saveFile(attachment.metadata.hash, {
key,
iv: attachment.iv,
name: attachment.metadata.filename,
type: attachment.metadata.type,
isUploaded: !!attachment.dateUploaded
})
);
}
type OutputTypeToReturnType = {
@@ -59,21 +61,27 @@ type OutputTypeToReturnType = {
export async function downloadAttachment<
TType extends "blob" | "base64" | "text",
TOutputType = OutputTypeToReturnType[TType]
>(hash: string, type: TType): Promise<TOutputType | undefined> {
const response = await download(hash);
>(
hash: string,
type: TType,
groupId?: string
): Promise<TOutputType | undefined> {
const response = await download(hash, groupId);
if (!response) return;
const { attachment, key } = response;
if (type === "base64" || type === "text")
return (await db.attachments?.read(hash, type)) as TOutputType;
const blob = await FS.decryptFile(attachment.metadata.hash, {
key,
iv: attachment.iv,
name: attachment.metadata.filename,
type: attachment.metadata.type,
isUploaded: !!attachment.dateUploaded
});
const blob = await lazify(import("../interfaces/fs"), ({ default: FS }) =>
FS.decryptFile(attachment.metadata.hash, {
key,
iv: attachment.iv,
name: attachment.metadata.filename,
type: attachment.metadata.type,
isUploaded: !!attachment.dateUploaded
})
);
if (!blob) return;
return blob as TOutputType;
@@ -84,7 +92,9 @@ export async function checkAttachment(hash: string) {
if (!attachment) return { failed: "Attachment not found." };
try {
const size = await FS.getUploadedFileSize(hash);
const size = await lazify(import("../interfaces/fs"), ({ default: FS }) =>
FS.getUploadedFileSize(hash)
);
if (size <= 0) return { failed: "File length is 0." };
} catch (e) {
return { failed: e instanceof Error ? e.message : "Unknown error." };

View File

@@ -21,6 +21,7 @@ import { EventSourcePolyfill as EventSource } from "event-source-polyfill";
import { DatabasePersistence, NNStorage } from "../interfaces/storage";
import { logger } from "../utils/logger";
import type Database from "@notesnook/core/dist/api";
import { showMigrationDialog } from "./dialog-controller";
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
@@ -70,7 +71,6 @@ async function initializeDatabase(persistence: DatabasePersistence) {
logger.measure("Database initialization");
if (db.migrations?.required()) {
const { showMigrationDialog } = await import("./dialog-controller");
await showMigrationDialog();
}

View File

@@ -17,13 +17,15 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { db } from "./db";
import { TaskManager } from "./task-manager";
import { zip } from "../utils/zip";
import { saveAs } from "file-saver";
import { ExportStream } from "../utils/streams/export-stream";
import { createZipStream } from "../utils/streams/zip-stream";
import { createWriteStream } from "../utils/stream-saver";
import { showToast } from "../utils/toast";
import { sanitizeFilename } from "@notesnook/common";
import Vault from "./vault";
import { db } from "./db";
import Note from "@notesnook/core/dist/models/note";
import { sanitizeFilename } from "@notesnook/common";
export async function exportToPDF(
title: string,
@@ -41,12 +43,21 @@ export async function exportToPDF(
iframe.style.width = "0";
iframe.style.height = "0";
iframe.style.border = "0";
iframe.loading = "eager";
iframe.onload = () => {
iframe.onload = async () => {
if (!iframe.contentWindow) return;
if (iframe.contentDocument) iframe.contentDocument.title = title;
iframe.contentWindow.onbeforeunload = () => closePrint(false);
iframe.contentWindow.onafterprint = () => closePrint(true);
if (
iframe.contentDocument &&
!!iframe.contentDocument.querySelector(".math-block,.math-inline")
) {
await new Promise((resolve) => setTimeout(resolve, 1000));
}
iframe.contentWindow.print();
};
@@ -60,7 +71,7 @@ export async function exportToPDF(
}
export async function exportNotes(
format: "pdf" | "md" | "txt" | "html",
format: "pdf" | "md" | "txt" | "html" | "md-frontmatter",
noteIds: string[]
): Promise<boolean> {
return await TaskManager.startTask({
@@ -68,72 +79,91 @@ export async function exportNotes(
title: "Exporting notes",
subtitle: "Please wait while your notes are exported.",
action: async (report) => {
let vaultUnlocked = false;
if (noteIds.length === 1 && db.notes?.note(noteIds[0])?.data.locked) {
vaultUnlocked = await Vault.unlockVault();
if (!vaultUnlocked) return false;
} else if (noteIds.length > 1 && (await db.vault?.exists())) {
vaultUnlocked = await Vault.unlockVault();
if (!vaultUnlocked)
showToast(
"error",
"Failed to unlock vault. Locked notes will be skipped."
);
try {
let progress = 0;
await createNoteStream(noteIds)
.pipeThrough(
new TransformStream<Note, Note>({
transform(note, controller) {
controller.enqueue(note);
report({
total: noteIds.length,
current: progress++,
text: `Exporting "${note?.title || "Unknown note"}"`
});
}
})
)
.pipeThrough(new ExportStream(format))
.pipeThrough(createZipStream())
.pipeTo(await createWriteStream("notes.zip"));
return true;
} catch (e) {
if (e instanceof Error) showToast("error", e.message);
}
const files = [];
let index = 0;
for (const noteId of noteIds) {
const note = db.notes?.note(noteId);
if (!note) continue;
if (!vaultUnlocked && note.data.locked) continue;
report({
current: ++index,
total: noteIds.length,
text: `Exporting "${note.title}"...`
});
const rawContent = await db.content?.raw(note.data.contentId);
const content = note.data.locked
? await db.vault?.decryptContent(rawContent)
: rawContent;
const exported = await note
.export(format === "pdf" ? "html" : format, content)
.catch((e: Error) => {
console.error(note.data, e);
showToast(
"error",
`Failed to export note "${note.title}": ${e.message}`
);
});
if (typeof exported !== "string") {
showToast("error", `Failed to export note "${note.title}"`);
continue;
}
if (format === "pdf") {
return await exportToPDF(note.title, exported);
}
files.push({ filename: note.title, content: exported });
}
if (!files.length) return false;
if (files.length === 1) {
saveAs(
new Blob([Buffer.from(files[0].content, "utf-8")]),
`${sanitizeFilename(files[0].filename)}.${format}`
);
} else {
const zipped = await zip(files, format);
saveAs(new Blob([zipped.buffer]), "notes.zip");
}
return true;
return false;
}
});
}
function createNoteStream(noteIds: string[]) {
let i = 0;
return new ReadableStream<Note>({
start() {},
async pull(controller) {
const noteId = noteIds[i++];
if (!noteId) controller.close();
else controller.enqueue(db.notes?.note(noteId));
},
async cancel(reason) {
throw new Error(reason);
}
});
}
const FORMAT_TO_EXT = {
pdf: "pdf",
md: "md",
txt: "txt",
html: "html",
"md-frontmatter": "md"
} as const;
export async function exportNote(
note: Note,
format: keyof typeof FORMAT_TO_EXT,
disableTemplate = false
) {
if (!db.vault?.unlocked && note.data.locked && !(await Vault.unlockVault())) {
showToast("error", `Skipping note "${note.title}" as it is locked.`);
return false;
}
const rawContent = note.data.contentId
? await db.content?.raw(note.data.contentId)
: undefined;
const content =
rawContent &&
!rawContent.deleted &&
(typeof rawContent.data === "object"
? await db.vault?.decryptContent(rawContent)
: rawContent);
const exported = await note
.export(format === "pdf" ? "html" : format, content, disableTemplate)
.catch((e: Error) => {
console.error(note.data, e);
showToast("error", `Failed to export note "${note.title}": ${e.message}`);
return false as const;
});
if (typeof exported === "boolean" && !exported) return false;
const filename = sanitizeFilename(note.title, { replacement: "-" });
const ext = FORMAT_TO_EXT[format];
return {
filename: [filename, ext].join("."),
content: exported
};
}

View File

@@ -36,9 +36,9 @@ import { TaskManager } from "./task-manager";
import { EVENTS } from "@notesnook/core/dist/common";
import { getFormattedDate } from "@notesnook/common";
import { createWritableStream } from "./desktop-bridge";
import { ZipStream } from "../utils/streams/zip-stream";
import { createZipStream } from "../utils/streams/zip-stream";
import { FeatureKeys } from "../dialogs/feature-dialog";
import { Entry, Reader } from "../utils/zip-reader";
import { ZipEntry, createUnzipIterator } from "../utils/streams/unzip-stream";
export const CREATE_BUTTON_MAP = {
notes: {
@@ -113,7 +113,7 @@ export async function createBackup() {
controller.close();
}
})
.pipeThrough(new ZipStream())
.pipeThrough(createZipStream())
.pipeTo(writeStream);
}
});
@@ -138,8 +138,9 @@ export async function selectBackupFile() {
export async function importBackup() {
const backupFile = await selectBackupFile();
if (!backupFile) return;
if (!backupFile) return false;
await restoreBackupFile(backupFile);
return true;
}
export async function restoreBackupFile(backupFile: File) {
@@ -165,12 +166,12 @@ export async function restoreBackupFile(backupFile: File) {
type: "modal",
action: async (report) => {
let cachedPassword: string | undefined = undefined;
const { read, totalFiles } = await Reader(backupFile);
const entries: Entry[] = [];
// const { read, totalFiles } = await Reader(backupFile);
const entries: ZipEntry[] = [];
let filesProcessed = 0;
let isValid = false;
for await (const entry of read()) {
for await (const entry of createUnzipIterator(backupFile)) {
if (entry.name === ".nnbackup") {
isValid = true;
continue;
@@ -199,7 +200,6 @@ export async function restoreBackupFile(backupFile: File) {
}
report({
total: totalFiles,
text: `Processed ${entry.name}`,
current: filesProcessed++
});

View File

@@ -35,20 +35,11 @@ type Item = {
};
async function moveNotesToTrash(notes: Item[], confirm = true) {
const item = notes[0];
if (confirm && !(await showMultiDeleteConfirmation(notes.length))) return;
if (notes.length === 1) {
if (
item.locked &&
!(await Vault.unlockNote(item.id, "unlock_and_delete_note"))
)
return;
item.locked = false;
}
if (notes.some((n) => n.locked) && !(await Vault.unlockVault())) return;
const items = notes.map((item) => {
if (item.locked || db.monographs?.isPublished(item.id)) return 0;
if (db.monographs?.isPublished(item.id)) return 0;
return item.id;
});

View File

@@ -291,7 +291,9 @@ function CalltoAction({ action, variant, sx, dismissAnnouncement }) {
case "link": {
const url = new URL(action.data);
const target =
url.origin === window.location.origin ? "_self" : "_blank";
url.origin === window.location.origin && !IS_DESKTOP_APP
? "_self"
: "_blank";
window.open(action.data, target, "noopener noreferrer");
break;
}

View File

@@ -164,7 +164,11 @@ export function Attachment({
<AttachmentError
color={"icon-error"}
size={16}
title={attachment.failed}
title={
typeof attachment.failed === "object"
? attachment.failed.toString()
: attachment.failed
}
/>
) : attachment.working ? (
<Loading size={16} />
@@ -290,7 +294,6 @@ const AttachmentMenuItems: (
key: "download",
title: status?.type === "download" ? "Cancel download" : "Download",
icon: Download.path,
isDisabled: !attachment.dateUploaded,
onClick: async () => {
const isDownloading = status?.type === "download";
if (isDownloading) {

View File

@@ -171,8 +171,10 @@ function AuthContainer(props) {
{testimonial.text} {" "}
<Link
sx={{ fontStyle: "italic", color: "paragraph-secondary" }}
href={testimonial.link}
>
href={testimonial.link}
target="_blank"
rel="noopener noreferrer"
>
source
</Link>
</Text>

View File

@@ -80,11 +80,10 @@ function BaseDialog(props: React.PropsWithChildren<DialogProps>) {
border: 0,
zIndex: 999,
backgroundColor: "var(--backdrop)"
},
overlay: {
opacity: 1
}
// overlay: {
// zIndex: 999,
// background: "var(--backdrop)"
// }
}}
>
<ScopedThemeProvider

View File

@@ -26,6 +26,7 @@ import IconTag from "../icon-tag";
import { db } from "../../common/db";
import { useMenuTrigger } from "../../hooks/use-menu";
import { MenuItem } from "@notesnook/ui";
import { navigate } from "../../navigation";
type HeaderProps = { readonly: boolean };
function Header(props: HeaderProps) {
@@ -39,7 +40,7 @@ function Header(props: HeaderProps) {
return (
<>
{!readonly && id && (
{id && (
<Flex
sx={{ lineHeight: 2.5, alignItems: "center", flexWrap: "wrap" }}
data-test-id="tags"
@@ -50,24 +51,34 @@ function Header(props: HeaderProps) {
key={tag}
text={db.tags?.alias(tag)}
icon={Tag}
title={`Click to remove`}
onClick={() => setTag(tag)}
title={tag}
onClick={() => {
const tagItem = db.tags?.tag(tag);
if (!tagItem) {
setTag(tag);
return;
}
navigate(`/tags/${tagItem.id}`);
}}
onDismiss={readonly ? undefined : () => setTag(tag)}
styles={{ container: { mr: 1 }, text: { fontSize: "body" } }}
/>
))}
<Autosuggest
sessionId={id}
filter={(query) =>
db.lookup?.tags(filterableTags, query).slice(0, 10) || []
}
onAdd={(value) => setTag(value)}
onSelect={(item) => setTag(item.title)}
onRemove={() => {
if (tags.length <= 0) return;
setTag(tags[tags.length - 1]);
}}
defaultItems={filterableTags?.slice(0, 10) || []}
/>
{!readonly && (
<Autosuggest
sessionId={id}
filter={(query) =>
db.lookup?.tags(filterableTags, query).slice(0, 10) || []
}
onAdd={(value) => setTag(value)}
onSelect={(item) => setTag(item.title)}
onRemove={() => {
if (tags.length <= 0) return;
setTag(tags[tags.length - 1]);
}}
defaultItems={filterableTags?.slice(0, 10) || []}
/>
)}
</Flex>
)}
</>
@@ -86,6 +97,7 @@ type AutosuggestProps = {
export function Autosuggest(props: AutosuggestProps) {
const { sessionId, filter, onRemove, onSelect, onAdd, defaultItems } = props;
const inputRef = useRef<HTMLInputElement>(null);
const arrowDown = useRef<boolean>();
const filteredItems = useRef<any[]>([]);
const { openMenu, closeMenu, isOpen } = useMenuTrigger();
const clearInput = useCallback(() => {
@@ -178,8 +190,13 @@ export function Autosuggest(props: AutosuggestProps) {
data-test-id="editor-tag-input"
onFocus={() => {
const text = getInputValue();
console.log(defaultItems);
if (!text) onOpenMenu(defaultItems.slice());
else onOpenMenu([]);
}}
onClick={() => {
const text = getInputValue();
if (!text) onOpenMenu(defaultItems.slice());
else onOpenMenu([]);
}}
onChange={(e) => {
const { value } = e.target;
@@ -191,22 +208,17 @@ export function Autosuggest(props: AutosuggestProps) {
}}
onKeyDown={(e) => {
const text = getInputValue();
if (
e.key === "Enter" &&
!!text &&
isOpen &&
filteredItems.current.length <= 0
) {
if (e.key === "Enter" && !!text && isOpen && !arrowDown.current) {
onAction("add", text);
} else if (e.key === "Enter" && !!text && isOpen) {
onAction("select", filteredItems.current[0]);
} else if (!text && e.key === "Backspace") {
onRemove();
closeMenu();
} else if (e.key === "Escape") {
arrowDown.current = false;
closeMenu();
e.stopPropagation();
} else if (e.key === "ArrowUp" || e.key === "ArrowDown") {
arrowDown.current = true;
if (e.key === "ArrowDown" && !text) onOpenMenu(defaultItems.slice());
e.preventDefault();

View File

@@ -59,7 +59,7 @@ import { getFormattedDate } from "@notesnook/common";
const PDFPreview = React.lazy(() => import("../pdf-preview"));
type PreviewSession = {
content: { data: string; type: string };
content: { data: string; type: string; title: string };
dateCreated: number;
dateEdited: number;
};
@@ -72,11 +72,12 @@ type DocumentPreview = {
function onEditorChange(
noteId: string | undefined,
sessionId: number,
content: string
content: string,
ignoreEdit: boolean
) {
if (!content) return;
editorstore.get().saveSessionContent(noteId, sessionId, {
editorstore.get().saveSessionContent(noteId, sessionId, ignoreEdit, {
type: "tiptap",
data: content
});
@@ -107,6 +108,7 @@ export default function EditorManager({
const arePropertiesVisible = useStore((store) => store.arePropertiesVisible);
const toggleProperties = useStore((store) => store.toggleProperties);
const isReadonly = useStore((store) => store.session.readonly);
const setPreviewTitle = useStore((store) => store.setPreviewTitle);
const isFocusMode = useAppStore((store) => store.isFocusMode);
const isPreviewSession = !!previewSession.current;
@@ -135,26 +137,7 @@ export default function EditorManager({
if (result) item.data = result.data;
else EV.publish(EVENTS.vaultLocked);
}
const oldHashes = editorInstance.current.getMediaHashes();
editorInstance.current.updateContent(item.data as string);
const newHashes = editorInstance.current.getMediaHashes();
const hashesToLoad = newHashes.filter(
(hash, index) => hash !== oldHashes[index]
);
if (appstore.get().isSyncing()) {
db.eventManager.subscribe(
EVENTS.syncCompleted,
async () => {
await db.attachments?.downloadMedia(id, hashesToLoad);
},
true
);
} else {
await db.attachments?.downloadMedia(id, hashesToLoad);
}
} else if (isNote) {
if (!locked && item.locked) return EV.publish(EVENTS.vaultLocked);
@@ -175,23 +158,12 @@ export default function EditorManager({
const openSession = useCallback(async (noteId: string | number) => {
await editorstore.get().openSession(noteId);
previewSession.current = undefined;
setPreviewTitle(undefined);
lastSavedTime.current = Date.now();
setTimestamp(Date.now());
}, []);
const loadMedia = useCallback(async () => {
if (previewSession.current) {
await db.content?.downloadMedia(
noteId,
previewSession.current.content,
true
);
} else if (noteId && editorstore.get().session.content) {
await db.attachments?.downloadMedia(noteId);
}
}, [noteId]);
useEffect(() => {
if (!isNewSession) return;
@@ -237,6 +209,7 @@ export default function EditorManager({
/>
)}
<Editor
id={noteId}
nonce={timestamp}
content={() =>
previewSession.current?.content?.data ||
@@ -247,7 +220,6 @@ export default function EditorManager({
options={{
readonly: isReadonly || isPreviewSession,
onRequestFocus: () => toggleProperties(false),
onLoadMedia: loadMedia,
focusMode: isFocusMode,
isMobile: isMobile || isTablet
}}
@@ -258,6 +230,7 @@ export default function EditorManager({
onOpenPreviewSession={async (session: PreviewSession) => {
previewSession.current = session;
setTimestamp(Date.now());
setPreviewTitle(previewSession.current.content.title);
}}
/>
)}
@@ -351,9 +324,9 @@ type EditorOptions = {
readonly?: boolean;
focusMode?: boolean;
onRequestFocus?: () => void;
onLoadMedia?: () => void;
};
type EditorProps = {
id: string | number;
content: () => string | undefined;
nonce?: number;
options?: EditorOptions;
@@ -361,8 +334,9 @@ type EditorProps = {
onPreviewDocument?: (preview: DocumentPreview) => void;
};
export function Editor(props: EditorProps) {
const { content, nonce, options, onContentChange, onPreviewDocument } = props;
const { readonly, headless, onLoadMedia, isMobile } = options || {
const { id, content, nonce, options, onContentChange, onPreviewDocument } =
props;
const { readonly, headless, isMobile } = options || {
headless: false,
readonly: false,
focusMode: false,
@@ -375,40 +349,16 @@ export function Editor(props: EditorProps) {
useEffect(() => {
const event = AppEventManager.subscribe(
AppEvents.UPDATE_ATTACHMENT_PROGRESS,
({ hash, loaded, total, type }: AttachmentProgress) => {
({ hash, loaded, total }: AttachmentProgress) => {
editor.current?.sendAttachmentProgress(
hash,
type,
Math.round((loaded / total) * 100)
);
}
);
const mediaAttachmentDownloadedEvent = EV.subscribe(
EVENTS.mediaAttachmentDownloaded,
({
groupId,
hash,
attachmentType,
src
}: {
groupId?: string;
attachmentType: "image" | "webclip" | "generic";
hash: string;
src: string;
}) => {
if (groupId?.startsWith("monograph")) return;
if (attachmentType === "image") {
editor.current?.loadImage(hash, src);
} else if (attachmentType === "webclip") {
editor.current?.loadWebClip(hash, src);
}
}
);
return () => {
event.unsubscribe();
mediaAttachmentDownloadedEvent.unsubscribe();
};
}, [editor]);
@@ -424,19 +374,23 @@ export function Editor(props: EditorProps) {
corsHost: Config.get("corsProxy", "https://cors.notesnook.com")
}}
onLoad={() => {
if (onLoadMedia) onLoadMedia();
if (nonce && nonce > 0) setIsLoading(false);
}}
onContentChange={onContentChange}
onChange={onEditorChange}
onDownloadAttachment={(attachment) => saveAttachment(attachment.hash)}
onPreviewAttachment={async ({ hash, dataurl }) => {
onPreviewAttachment={async (data) => {
const { hash } = data;
const attachment = db.attachments?.attachment(hash);
if (attachment && attachment.metadata.type.startsWith("image/")) {
const container = document.getElementById("dialogContainer");
if (!(container instanceof HTMLElement)) return;
dataurl = dataurl || (await downloadAttachment(hash, "base64"));
const dataurl = await downloadAttachment(
hash,
"base64",
id.toString()
);
if (!dataurl)
return showToast("error", "This image cannot be previewed.");
@@ -453,7 +407,7 @@ export function Editor(props: EditorProps) {
);
} else if (attachment && onPreviewDocument) {
onPreviewDocument({ hash });
const blob = await downloadAttachment(hash, "blob");
const blob = await downloadAttachment(hash, "blob", id.toString());
if (!blob) return;
onPreviewDocument({ url: URL.createObjectURL(blob), hash });
}
@@ -465,6 +419,13 @@ export function Editor(props: EditorProps) {
editor.current?.attachFile(file);
});
}}
onGetAttachmentData={(attachment) => {
return downloadAttachment(
attachment.hash,
attachment.type === "web-clip" ? "text" : "base64",
id.toString()
);
}}
onAttachFile={async (file) => {
const result = await attachFile(file);
if (!result) return;
@@ -555,7 +516,7 @@ function EditorChrome(
<Box
id="editorToolbar"
sx={{
display: readonly ? "none" : "flex",
display: "flex",
bg: "background",
position: "sticky",
top: 0,
@@ -578,7 +539,7 @@ function PreviewModeNotice(props: PreviewModeNoticeProps) {
async (cancelled: boolean) => {
const { id, sessionId } = editorstore.get().session;
if (!cancelled) {
await editorstore.saveSessionContent(id, sessionId, content);
await editorstore.saveSessionContent(id, sessionId, false, content);
}
onDiscard();
},

View File

@@ -23,9 +23,9 @@ import { db } from "../../common/db";
import { showBuyDialog } from "../../common/dialog-controller";
import { TaskManager } from "../../common/task-manager";
import { isUserPremium } from "../../hooks/use-is-user-premium";
import fs from "../../interfaces/fs";
import { showToast } from "../../utils/toast";
import { showFilePicker } from "../../utils/file-picker";
import { Attachment } from "@notesnook/editor";
const FILE_SIZE_LIMIT = 500 * 1024 * 1024;
const IMAGE_SIZE_LIMIT = 50 * 1024 * 1024;
@@ -81,33 +81,54 @@ export async function reuploadAttachment(
}
/**
* @param {File} selectedFile
* @param {File} file
* @returns
*/
async function pickFile(selectedFile: File, options?: AddAttachmentOptions) {
async function pickFile(
file: File,
options?: AddAttachmentOptions
): Promise<Attachment | undefined> {
try {
if (selectedFile.size > FILE_SIZE_LIMIT)
if (file.size > FILE_SIZE_LIMIT)
throw new Error("File too big. You cannot add files over 500 MB.");
if (!selectedFile) return;
if (!file) return;
return await addAttachment(selectedFile, undefined, options);
const hash = await addAttachment(file, options);
return {
type: "file",
filename: file.name,
hash,
mime: file.type,
size: file.size
};
} catch (e) {
showToast("error", `${(e as Error).message}`);
}
}
/**
* @param {File} selectedImage
* @param {File} file
* @returns
*/
async function pickImage(selectedImage: File, options?: AddAttachmentOptions) {
async function pickImage(
file: File,
options?: AddAttachmentOptions
): Promise<Attachment | undefined> {
try {
if (selectedImage.size > IMAGE_SIZE_LIMIT)
if (file.size > IMAGE_SIZE_LIMIT)
throw new Error("Image too big. You cannot add images over 50 MB.");
if (!selectedImage) return;
if (!file) return;
const dataurl = await toDataURL(selectedImage);
return await addAttachment(selectedImage, dataurl, options);
const hash = await addAttachment(file, options);
const dimensions = await getImageDimensions(file);
return {
type: "image",
filename: file.name,
hash,
mime: file.type,
size: file.size,
...dimensions
};
} catch (e) {
showToast("error", (e as Error).message);
}
@@ -119,12 +140,6 @@ async function getEncryptionKey(): Promise<SerializedKey> {
return key;
}
async function toDataURL(file: File): Promise<string> {
const buffer = await file.arrayBuffer();
const base64 = Buffer.from(buffer).toString("base64");
return `data:${file.type};base64,${base64}`;
}
export type AttachmentProgress = {
hash: string;
type: "encrypt" | "download" | "upload";
@@ -132,14 +147,6 @@ export type AttachmentProgress = {
loaded: number;
};
export type Attachment = {
hash: string;
filename: string;
mime: string;
size: number;
dataurl?: string;
};
type AddAttachmentOptions = {
expectedFileHash?: string;
showProgress?: boolean;
@@ -148,28 +155,31 @@ type AddAttachmentOptions = {
async function addAttachment(
file: File,
dataurl: string | undefined,
options: AddAttachmentOptions = {}
): Promise<Attachment> {
const { expectedFileHash, forceWrite, showProgress = true } = options;
): Promise<string> {
const { default: FS } = await import("../../interfaces/fs");
const { expectedFileHash, showProgress = true } = options;
let forceWrite = options.forceWrite;
const action = async () => {
const reader: ReadableStreamReader<Uint8Array> = (
file.stream() as unknown as ReadableStream<Uint8Array>
).getReader();
const { hash, type: hashType } = await fs.hashStream(reader);
const reader = file.stream().getReader();
const { hash, type: hashType } = await FS.hashStream(reader);
reader.releaseLock();
if (expectedFileHash && hash !== expectedFileHash)
throw new Error(
`Please select the same file for reuploading. Expected hash ${expectedFileHash} but got ${hash}.`
);
const exists = db.attachments?.exists(hash);
const exists = db.attachments?.attachment(hash);
if (!forceWrite && exists) {
forceWrite = (await FS.getUploadedFileSize(hash)) <= 0;
}
if (forceWrite || !exists) {
const key: SerializedKey = await getEncryptionKey();
const output = await fs.writeEncryptedFile(file, key, hash);
const output = await FS.writeEncryptedFile(file, key, hash);
if (!output) throw new Error("Could not encrypt file.");
if (forceWrite && exists) await db.attachments?.reset(hash);
@@ -177,19 +187,13 @@ async function addAttachment(
...output,
hash,
hashType,
filename: file.name,
type: file.type,
filename: exists?.metadata.filename || file.name,
type: exists?.metadata.type || file.type,
key
});
}
return {
hash: hash,
filename: file.name,
mime: file.type,
size: file.size,
dataurl
};
return hash;
};
const result = showProgress
@@ -200,7 +204,10 @@ async function addAttachment(
return result;
}
function withProgress<T>(file: File, action: () => Promise<T>): Promise<T> {
function withProgress<T>(
file: File,
action: () => Promise<T>
): Promise<T | Error> {
return TaskManager.startTask({
type: "modal",
title: "Encrypting attachment",
@@ -222,3 +229,19 @@ function withProgress<T>(file: File, action: () => Promise<T>): Promise<T> {
}
});
}
function getImageDimensions(file: File) {
return new Promise<{ width: number; height: number } | undefined>(
(resolve) => {
const img = new Image();
img.onload = () => {
const { naturalWidth: width, naturalHeight: height } = img;
resolve({ width, height });
};
img.onerror = () => {
resolve(undefined);
};
img.src = URL.createObjectURL(file);
}
);
}

View File

@@ -34,11 +34,11 @@ import {
getTotalWords,
countWords,
getFontById,
TiptapOptions
TiptapOptions,
Attachment
} from "@notesnook/editor";
import { Box, Flex } from "@theme-ui/components";
import { PropsWithChildren, useEffect, useMemo, useRef, useState } from "react";
import { Attachment } from "./picker";
import { IEditor } from "./types";
import {
useConfigureEditor,
@@ -61,7 +61,8 @@ import { useStore as useThemeStore } from "../../stores/theme-store";
type OnChangeHandler = (
id: string | undefined,
sessionId: number,
content: string
content: string,
ignoreEdit: boolean
) => void;
type TipTapProps = {
editorContainer: HTMLElement;
@@ -71,6 +72,7 @@ type TipTapProps = {
onInsertAttachment?: (type: AttachmentType) => void;
onDownloadAttachment?: (attachment: Attachment) => void;
onPreviewAttachment?: (attachment: Attachment) => void;
onGetAttachmentData?: (attachment: Attachment) => Promise<string | undefined>;
onAttachFile?: (file: File) => void;
onFocus?: () => void;
content?: () => string | undefined;
@@ -91,6 +93,7 @@ function save(
editor: Editor,
content: Fragment,
preventSave: boolean,
ignoreEdit: boolean,
onChange?: OnChangeHandler
) {
configureEditor({
@@ -104,7 +107,7 @@ function save(
if (preventSave) return;
const html = getHTMLFromFragment(content, editor.schema);
onChange?.(noteId, sessionId, html);
onChange?.(noteId, sessionId, html, ignoreEdit);
}
const deferredSave = debounceWithId(save, SAVE_INTERVAL);
@@ -116,6 +119,7 @@ function TipTap(props: TipTapProps) {
onInsertAttachment,
onDownloadAttachment,
onPreviewAttachment,
onGetAttachmentData,
onAttachFile,
onContentChange,
onFocus = () => {},
@@ -209,7 +213,8 @@ function TipTap(props: TipTapProps) {
onUpdate: ({ editor, transaction }) => {
onContentChange?.();
const preventSave = transaction?.getMeta("preventSave") as boolean;
const preventSave = transaction.getMeta("preventSave") as boolean;
const ignoreEdit = transaction.getMeta("ignoreEdit") as boolean;
const { id, sessionId } = editorstore.get().session;
const content = editor.state.doc.content;
deferredSave(
@@ -219,6 +224,7 @@ function TipTap(props: TipTapProps) {
editor as Editor,
content,
preventSave || !editor.isEditable,
ignoreEdit,
onChange
);
},
@@ -281,7 +287,8 @@ function TipTap(props: TipTapProps) {
onOpenLink: (url) => {
window.open(url, "_blank");
return true;
}
},
getAttachmentData: onGetAttachmentData
};
}, [readonly, nonce, doubleSpacedLines, dateFormat, timeFormat]);
@@ -419,14 +426,6 @@ function toIEditor(editor: Editor): IEditor {
}),
undo: () => editor.current?.commands.undo(),
redo: () => editor.current?.commands.redo(),
getMediaHashes: () => {
if (!editor.current) return [];
const hashes: string[] = [];
editor.current.state.doc.descendants((n) => {
if (typeof n.attrs.hash === "string") hashes.push(n.attrs.hash);
});
return hashes;
},
updateContent: (content) => {
const { from, to } = editor.state.selection;
editor.current
@@ -442,27 +441,17 @@ function toIEditor(editor: Editor): IEditor {
})
.run();
},
attachFile: (file: Attachment) => {
if (file.dataurl) {
editor.current?.commands.insertImage({
...file,
dataurl: file.dataurl
});
} else editor.current?.commands.insertAttachment(file);
},
loadWebClip: (hash, src) =>
editor.current?.commands.updateWebClip({ hash }, { src }),
loadImage: (hash, dataurl) =>
editor.current?.commands.updateImage(
{ hash },
{ hash, dataurl, preventUpdate: true }
),
sendAttachmentProgress: (hash, type, progress) =>
editor.current?.commands.setAttachmentProgress({
hash,
type,
progress
})
attachFile: (file: Attachment) =>
file.type === "image"
? editor.current?.commands.insertImage(file)
: editor.current?.commands.insertAttachment(file),
sendAttachmentProgress: (hash, progress) =>
editor.current?.commands.updateAttachment(
{
progress
},
{ query: (a) => a.hash === hash, preventUpdate: true }
)
};
}

View File

@@ -26,6 +26,8 @@ import useTablet from "../../hooks/use-tablet";
import { useEditorConfig } from "./context";
import { getFontById } from "@notesnook/editor";
import { AppEventManager, AppEvents } from "../../common/app-events";
import { replaceDateTime } from "@notesnook/editor/dist/extensions/date-time";
import { useStore as useSettingsStore } from "../../stores/setting-store";
type TitleBoxProps = {
readonly: boolean;
@@ -35,9 +37,13 @@ function TitleBox(props: TitleBoxProps) {
const { readonly } = props;
const inputRef = useRef<HTMLInputElement>(null);
const id = useStore((store) => store.session.id);
const previewTitle = useStore((store) => store.previewTitle);
const isMobile = useMobile();
const isTablet = useTablet();
const { editorConfig } = useEditorConfig();
const dateFormat = useSettingsStore((store) => store.dateFormat);
const timeFormat = useSettingsStore((store) => store.timeFormat);
const fontFamily = useMemo(
() => getFontById(editorConfig.fontFamily)?.font || "heading",
[editorConfig.fontFamily]
@@ -58,9 +64,9 @@ function TitleBox(props: TitleBoxProps) {
useEffect(() => {
if (!inputRef.current) return;
const { title } = useStore.getState().session;
inputRef.current.value = title;
inputRef.current.value = previewTitle || title;
updateFontSize(title.length);
}, [id, updateFontSize]);
}, [id, updateFontSize, previewTitle]);
useEffect(() => {
if (!inputRef.current) return;
@@ -111,6 +117,11 @@ function TitleBox(props: TitleBoxProps) {
}}
onChange={(e) => {
const { sessionId, id } = store.get().session;
e.target.value = replaceDateTime(
e.target.value,
dateFormat,
timeFormat
);
debouncedOnTitleChange(sessionId, id, e.target.value);
updateFontSize(e.target.value.length);
}}

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 { useEffect, useMemo, useState } from "react";
import { useEffect, useMemo, useRef, useState } from "react";
import { Button, Flex, Text } from "@theme-ui/components";
import {
Published,
@@ -51,6 +51,7 @@ function Toolbar() {
const sessionId = useStore((store) => store.session.id);
const isDeleted = useStore((store) => store.session.isDeleted);
const isLocked = useStore((store) => store.session.locked);
const readonly = useStore((store) => store.session.readonly);
const [isFullscreen, setIsFullscreen] = useState(false);
const isFocusMode = useAppStore((store) => store.isFocusMode);
const toggleFocusMode = useAppStore((store) => store.toggleFocusMode);
@@ -62,6 +63,7 @@ function Toolbar() {
const theme = useThemeStore((store) => store.colorScheme);
const toggleNightMode = useThemeStore((store) => store.toggleColorScheme);
const [isTitleVisible, setIsTitleVisible] = useState(false);
const onScrollTitleRef = useRef(null);
const monographs = useMonographStore((store) => store.monographs);
const { canRedo, canUndo, redo, undo } = useHistory();
@@ -91,6 +93,10 @@ function Toolbar() {
};
}, [isTitleVisible]);
useEffect(() => {
if (onScrollTitleRef.current) onScrollTitleRef.current.value = title;
}, [title]);
const tools = useMemo(
() => [
{
@@ -215,6 +221,7 @@ function Toolbar() {
}}
/>
<AnimatedInput
ref={onScrollTitleRef}
variant="clean"
ml={[2, 2, 0]}
initial={{
@@ -227,6 +234,7 @@ function Toolbar() {
}}
transition={{ duration: 0.5 }}
defaultValue={title}
readOnly={readonly}
onChange={(e) => {
AppEventManager.publish(AppEvents.changeNoteTitle, {
title: e.target.value,

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 { Attachment } from "./picker";
import { Attachment } from "@notesnook/editor";
export type NoteStatistics = {
words: {
@@ -33,14 +33,7 @@ export interface IEditor {
}) => void;
undo: () => void;
redo: () => void;
getMediaHashes: () => string[];
updateContent: (content: string) => void;
attachFile: (file: Attachment) => void;
loadWebClip: (hash: string, html: string) => void;
loadImage: (hash: string, src: string) => void;
sendAttachmentProgress: (
hash: string,
type: "download" | "upload" | "encrypt",
progress: number
) => void;
sendAttachmentProgress: (hash: string, progress: number) => void;
}

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