Compare commits

...
Author SHA1 Message Date
StandardNotes CI 0e10b347b6 chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].0
 - @standardnotes/[email protected].3
 - @standardnotes/[email protected].3
 - @standardnotes/[email protected].9
 - @standardnotes/[email protected].2
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].4
 - @standardnotes/[email protected].0
 - @standardnotes/[email protected].1
 - @standardnotes/[email protected].0
2022-12-05 16:06:51 +00:00
Mo caf2c4a876 feat: add Super note type to list of note types (#2086) 2022-12-05 09:38:42 -06:00
Mo 1d22365086 fix: fixes issue where first change was ignored with Super notes 2022-12-05 07:56:10 -06:00
Mo bd5ccbfab6 refactor: avoid deallocing note controller until local propagation complete 2022-12-05 07:53:21 -06:00
StandardNotes CI 46b19d8caf chore(release): publish
- @standardnotes/[email protected].9
 - @standardnotes/[email protected].8
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].1
2022-12-05 13:22:42 +00:00
Mo a6a0d615c5 fix: fixes issue where toggled-off panes would reappear after making selection 2022-12-05 06:55:39 -06:00
StandardNotes CI f3203e2e80 chore(release): publish
- @standardnotes/[email protected].8
 - @standardnotes/[email protected].7
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].0
 - @standardnotes/[email protected].0
2022-12-05 09:41:22 +00:00
Aman Harwara e0edabb4b9 Merge branch 'main' of github.com:standardnotes/app 2022-12-05 14:17:36 +05:30
Aman Harwara e68c708628 feat: Added keyboard shortcut to toggle dark theme (Ctrl/Cmd+Shift+D) 2022-12-05 14:17:29 +05:30
StandardNotes CI e4f3f26937 chore(release): publish
- @standardnotes/[email protected].7
 - @standardnotes/[email protected].6
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].0
 - @standardnotes/[email protected].0
2022-12-05 08:34:49 +00:00
Aman Harwara 8575b3e714 feat: Show preferences keyboard shortcut in tooltip 2022-12-05 13:37:21 +05:30
Aman Harwara 7c261af230 feat: Added keyboard shortcut (Ctrl/Cmd+,) for opening preferences 2022-12-05 13:34:34 +05:30
Aman Harwara 71e20f2a2f fix: Fixed purchase flow padding 2022-12-05 13:18:22 +05:30
StandardNotes CI 3855b9f767 chore(release): publish
- @standardnotes/[email protected].6
 - @standardnotes/[email protected].5
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].3
 - @standardnotes/[email protected].4
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].5
2022-12-04 11:47:50 +00:00
Aman Harwara 5c29e22ea4 Merge branch 'main' of github.com:standardnotes/app 2022-12-04 16:49:55 +05:30
Aman Harwara 07cb00a815 fix(mobile): Fixed issue where pressing the back button on editor screen wouldn't switch back to the items list on Android 2022-12-04 16:49:46 +05:30
StandardNotes CI 4f2ee4b56f chore(release): publish
- @standardnotes/[email protected].5
 - @standardnotes/[email protected].4
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].4
2022-12-04 06:06:27 +00:00
Aman Harwara 90e10c76f7 refactor: separate selectAndUploadFile and uploadFile functions (#2085) 2022-12-04 11:11:37 +05:30
StandardNotes CI 2175c9cdcb chore(release): publish
- @standardnotes/[email protected].4
 - @standardnotes/[email protected].3
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].2
 - @standardnotes/[email protected].3
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].3
2022-12-02 17:31:37 +00:00
Mo 8c41f0ecc3 fix: fixes issue where Moments camera wouldn't properly start on desktop app (#2084) 2022-12-02 11:05:32 -06:00
StandardNotes CI c8c166d2a0 chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected].3
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].2
 - @standardnotes/[email protected].2
 - @standardnotes/[email protected].2
 - @standardnotes/[email protected].1
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].1
 - @standardnotes/[email protected].2
 - @standardnotes/[email protected].9
 - @standardnotes/[email protected].1
 - @standardnotes/[email protected].2
2022-12-02 16:44:12 +00:00
Aman Harwara 6cacdd8c58 fix(mobile): Fixes issue where file upload doesn't work with immediate biometrics (#2083) 2022-12-02 21:48:56 +05:30
Mo b5b6192b60 fix: fixes issue where files imported from another account could not be deleted (#2082) 2022-12-02 10:13:25 -06:00
StandardNotes CI 635e2d9150 chore(release): publish
- @standardnotes/[email protected].2
 - @standardnotes/[email protected].1
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].1
2022-12-02 15:59:51 +00:00
Aman Harwara 0c23e4d36c fix(mobile): PDF preview causing loop when biometrics are set to Immediate (#2080) 2022-12-02 21:02:52 +05:30
StandardNotes CI 1e8ce4bee6 chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected].1
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].1
 - @standardnotes/[email protected].1
 - @standardnotes/[email protected].0
 - @standardnotes/[email protected].0
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].0
 - @standardnotes/[email protected].1
 - @standardnotes/[email protected].8
 - @standardnotes/[email protected].0
2022-12-02 15:13:17 +00:00
Mo 29368c51b8 feat: Moments: your personal photo journal, now available in Labs (#2079) 2022-12-02 08:41:21 -06:00
StandardNotes CI 621bf1b810 chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected].0
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].0
 - @standardnotes/[email protected].0
 - @standardnotes/[email protected].7
 - @standardnotes/[email protected].2
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].0
 - @standardnotes/[email protected].0
 - @standardnotes/[email protected].7
 - @standardnotes/[email protected].0
 - @standardnotes/[email protected].0
2022-12-01 18:22:56 +00:00
Mo 28e43d37c0 feat: download and preview files from local backups automatically, if a local backup is available (#2076) 2022-12-01 11:56:28 -06:00
StandardNotes CI e07fed267f chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected].2
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].5
 - @standardnotes/[email protected].5
 - @standardnotes/[email protected].6
 - @standardnotes/[email protected].1
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].1
 - @standardnotes/[email protected].1
 - @standardnotes/[email protected].6
 - @standardnotes/[email protected].1
 - @standardnotes/[email protected].2
2022-12-01 13:53:01 +00:00
Mo 1726302615 fix: fixes issue where Dynamic Theme did not properly work with new grid system 2022-12-01 07:21:30 -06:00
Mo 73ca12275f chore: fix date tests 2022-12-01 07:05:02 -06:00
Karol Sójko 1ee6be4e5c Revert "chore(temp): e2e tests run flag"
This reverts commit 0e95b451d6.
2022-12-01 10:47:35 +01:00
StandardNotes CI 42fc1d073d chore(release): publish
- @standardnotes/[email protected].1
 - @standardnotes/[email protected].5
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].1
2022-11-30 21:26:13 +00:00
Aman Harwara c32541c6e1 fix: Embedded image aspect ratio in Super notes (#2075) 2022-12-01 02:41:35 +05:30
StandardNotes CI 34d9100af6 chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected].0
 - @standardnotes/[email protected].4
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].0
2022-11-30 20:53:42 +00:00
Mo 7f2074a6ec feat: screen presentation and dismiss animations for mobile (#2073) 2022-11-30 14:37:36 -06:00
Mo 0e95b451d6 chore(temp): e2e tests run flag 2022-11-30 14:32:05 -06:00
Aman Harwara 0e886bf0f1 feat: Move editor apperance preferences to "Appearance" tab 2022-12-01 00:09:55 +05:30
Aman Harwara 9b3f11d85b fix: Disable all-caps for H2/H3 headings in Super notes 2022-11-30 23:30:57 +05:30
Aman Harwara 349fd7204e fix: Super note block drag-n-drop on mobile (#2072) 2022-11-30 22:20:46 +05:30
StandardNotes CI afa9aafa3e chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].3
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].3
2022-11-30 13:17:55 +00:00
Aman Harwara b57786eeeb Merge branch 'main' of github.com:standardnotes/app 2022-11-30 18:22:51 +05:30
Aman Harwara f30d0898ec fix: "Prevent editing" option not working correctly for Super notes 2022-11-30 18:22:41 +05:30
StandardNotes CI 1729bc4292 chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].2
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].2
2022-11-30 11:39:49 +00:00
Aman Harwara abe2b17cb4 Merge branch 'main' of github.com:standardnotes/app 2022-11-30 16:44:08 +05:30
Aman Harwara d51de34daa fix: Render super note content in revision history modal 2022-11-30 16:43:57 +05:30
StandardNotes CI ec0f0a2b18 chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].1
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].1
2022-11-29 16:54:01 +00:00
Aman Harwara a417e5ba8d fix: Make super note styling consistent with plaintext notes (#2068) 2022-11-29 21:57:23 +05:30
StandardNotes CI 02eb969e07 chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].4
 - @standardnotes/[email protected].4
 - @standardnotes/[email protected].0
 - @standardnotes/[email protected].0
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].0
 - @standardnotes/[email protected].0
 - @standardnotes/[email protected].5
 - @standardnotes/[email protected].0
2022-11-28 22:08:20 +00:00
Mo 767d354780 feat: display number of files for 'Files' view (#2065)
* feat: display number of files for 'Files' view

* feat: include files count in Preferences > Security
2022-11-28 15:38:50 -06:00
StandardNotes CI 052e7d2f90 chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].0
 - @standardnotes/[email protected].0
2022-11-28 19:07:10 +00:00
Aman Harwara e3ec8503a5 refactor: use ClipboardEvent interface for file paste command 2022-11-29 00:12:10 +05:30
Mo cc2762a29d feat: convert Super notes to Markdown behind the scenes while publishing to Listed (#2064) 2022-11-28 12:19:53 -06:00
Aman Harwara 96e8dfdd31 fix: Only autofocus super notes when creating new note (#2063) 2022-11-28 23:38:45 +05:30
StandardNotes CI 60cbd7aed2 chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].3
 - @standardnotes/[email protected].3
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].9
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].2
 - @standardnotes/[email protected].6
 - @standardnotes/[email protected].4
 - @standardnotes/[email protected].2
2022-11-28 17:43:50 +00:00
Mo 2b0c9b188c fix: exclude files from being counted in 'Notes' view (#2062) 2022-11-28 11:11:55 -06:00
StandardNotes CI 4181a4cca0 chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].3
 - @standardnotes/[email protected].1
2022-11-28 16:29:28 +00:00
Mo d51ab36bbf refactor: hide keyboard shortcuts if on mobile 2022-11-28 10:00:35 -06:00
StandardNotes CI 81774d74c6 chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].0
2022-11-28 15:40:46 +00:00
Aman Harwara 148aee441b fix: Super editor popover menus on iOS (#2061) 2022-11-28 20:36:10 +05:30
Aman Harwara db3069eab3 feat: Paste files in Super notes to upload and embed them (#2060) 2022-11-28 20:17:21 +05:30
StandardNotes CI c045eb1459 chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].2
 - @standardnotes/[email protected].2
2022-11-28 10:17:02 +00:00
Aman Harwara e19184c2da Merge branch 'main' of github.com:standardnotes/app 2022-11-28 15:15:18 +05:30
Aman Harwara 7f6c53dfcb fix: add new line after embedding files
fix: sync icon direction
2022-11-28 15:15:06 +05:30
StandardNotes CI 1c9ad49aea chore(release): publish
- @standardnotes/[email protected].9
 - @standardnotes/[email protected].9
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].1
2022-11-28 08:55:36 +00:00
Aman Harwara 6fc1bb5e84 fix: navigation footer padding 2022-11-28 13:59:41 +05:30
StandardNotes CI ddd7c8f889 chore(release): publish
- @standardnotes/[email protected].8
 - @standardnotes/[email protected].8
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].0
2022-11-27 21:13:25 +00:00
Aman Harwara 03eb1cc6b4 Merge branch 'main' of github.com:standardnotes/app 2022-11-28 02:18:03 +05:30
Aman Harwara ac91621271 feat: add arrow key navigation to file context menu & options panel
feat: close popover with Escape key and focus anchor element

fix: menu focus

fix: quick settings menu keyboard navigation
2022-11-28 02:17:55 +05:30
StandardNotes CI c01fa7bb99 chore(release): publish
- @standardnotes/[email protected].7
 - @standardnotes/[email protected].7
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].4
2022-11-27 18:30:00 +00:00
Aman Harwara 6db6079790 Merge branch 'main' of github.com:standardnotes/app 2022-11-27 23:33:35 +05:30
Aman Harwara 2026d8b936 refactor: note options tag selection menu 2022-11-27 23:33:26 +05:30
StandardNotes CI 645602b9d3 chore(release): publish
- @standardnotes/[email protected].6
 - @standardnotes/[email protected].6
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].3
2022-11-27 17:51:13 +00:00
Aman Harwara 3c91b0cb17 refactor: menu items (#2057) 2022-11-27 22:53:30 +05:30
StandardNotes CI 8145cdb8ac chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected].5
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].2
 - @standardnotes/[email protected].2
 - @standardnotes/[email protected].5
 - @standardnotes/[email protected].8
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].1
 - @standardnotes/[email protected].5
 - @standardnotes/[email protected].1
 - @standardnotes/[email protected].2
2022-11-27 11:55:50 +00:00
Aman Harwara d50c5fefcb chore: fix types 2022-11-27 16:59:58 +05:30
Aman Harwara 745e850a5c chore: fix types 2022-11-27 16:48:30 +05:30
Aman Harwara cadf03f0ef refactor: correct type autocomplete for <Icon/> type 2022-11-27 16:33:52 +05:30
StandardNotes CI 931b674e4e chore(release): publish
- @standardnotes/[email protected].4
 - @standardnotes/[email protected].4
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].1
2022-11-25 13:07:41 +00:00
Mo a6efc5336b fix: issue where plaintext editor autofocus would interfere with sign in form 2022-11-25 06:41:40 -06:00
StandardNotes CI 21611d9b45 chore(release): publish
- @standardnotes/[email protected].3
 - @standardnotes/[email protected].3
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].0
 - @standardnotes/[email protected].0
2022-11-25 08:57:51 +00:00
Karol Sójko 9b91875d3d Revert "chore: e2e tests opt in"
This reverts commit 4661c6cb77.
2022-11-25 09:32:28 +01:00
Aman Harwara dcc8cfbe45 feat: Markdown, HTML & JSON export options for super notes (#2054) 2022-11-25 13:58:44 +05:30
271 changed files with 5856 additions and 2122 deletions
+2 -1
View File
@@ -160,7 +160,6 @@ jobs:
run:
working-directory: packages/desktop
steps:
- run: echo APP_VERSION=$(node -p "require('./../web/package.json').version") >> $GITHUB_ENV
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
@@ -169,6 +168,8 @@ jobs:
node-version-file: '.nvmrc'
cache: 'yarn'
- run: echo APP_VERSION=$(node -p "require('./../web/package.json').version") >> $GITHUB_ENV
- run: yarn install --immutable
- uses: actions/download-artifact@v3
+4 -4
View File
@@ -50,20 +50,20 @@ jobs:
run: yarn test
- name: Login to Docker Hub
if: "${{ contains(github.event.head_commit.message, 'run e2e') == true }}"
if: "${{ contains(github.event.head_commit.message, 'skip e2e') == false }}"
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Publish SNJS Docker image for E2E testing
if: "${{ contains(github.event.head_commit.message, 'run e2e') == true }}"
if: "${{ contains(github.event.head_commit.message, 'skip e2e') == false }}"
run: |
yarn docker build @standardnotes/snjs -t standardnotes/snjs:${{ github.sha }}
docker push standardnotes/snjs:${{ github.sha }}
- name: Run E2E test suite
if: "${{ contains(github.event.head_commit.message, 'run e2e') == true }}"
if: "${{ contains(github.event.head_commit.message, 'skip e2e') == false }}"
uses: convictional/[email protected]
with:
owner: standardnotes
@@ -85,7 +85,7 @@ jobs:
NODE_AUTH_TOKEN: ${{ secrets.CI_NPM_TOKEN }}
- name: Publish SNJS Docker image as stable
if: "${{ contains(github.event.head_commit.message, 'run e2e') == true }}"
if: "${{ contains(github.event.head_commit.message, 'skip e2e') == false }}"
run: |
docker tag standardnotes/snjs:${{ github.sha }} standardnotes/snjs:latest
docker push standardnotes/snjs:latest
+32
View File
@@ -3,6 +3,38 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.20.18](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-05)
**Note:** Version bump only for package @standardnotes/api
## [1.20.17](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-02)
**Note:** Version bump only for package @standardnotes/api
## [1.20.16](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-02)
**Note:** Version bump only for package @standardnotes/api
## [1.20.15](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-01)
**Note:** Version bump only for package @standardnotes/api
## [1.20.14](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-01)
**Note:** Version bump only for package @standardnotes/api
## [1.20.13](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-28)
**Note:** Version bump only for package @standardnotes/api
## [1.20.12](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-28)
**Note:** Version bump only for package @standardnotes/api
## [1.20.11](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-27)
**Note:** Version bump only for package @standardnotes/api
## [1.20.10](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-23)
**Note:** Version bump only for package @standardnotes/api
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/api",
"version": "1.20.10",
"version": "1.20.18",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
+31
View File
@@ -3,6 +3,37 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.9.6](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-30)
### Bug Fixes
* Disable all-caps for H2/H3 headings in Super notes ([9b3f11d](https://github.com/standardnotes/app/commit/9b3f11d85b10914a03aab93803513766670c47fe))
* Super note block drag-n-drop on mobile ([#2072](https://github.com/standardnotes/app/issues/2072)) ([349fd72](https://github.com/standardnotes/app/commit/349fd7204e209d56b720a3b0cc6b2bfc0fc50f47))
## [1.9.5](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-30)
**Note:** Version bump only for package @standardnotes/blocks-editor
## [1.9.4](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-30)
**Note:** Version bump only for package @standardnotes/blocks-editor
## [1.9.3](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-29)
### Bug Fixes
* Make super note styling consistent with plaintext notes ([#2068](https://github.com/standardnotes/app/issues/2068)) ([a417e5b](https://github.com/standardnotes/app/commit/a417e5ba8d3fc224b1527d0a9cfb0f25d197f9de))
## [1.9.2](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-28)
### Bug Fixes
* Only autofocus super notes when creating new note ([#2063](https://github.com/standardnotes/app/issues/2063)) ([96e8dfd](https://github.com/standardnotes/app/commit/96e8dfdd310fad88a360c4ac984b376b51a618d1))
## [1.9.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-28)
**Note:** Version bump only for package @standardnotes/blocks-editor
# [1.9.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-23)
### Features
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/blocks-editor",
"version": "1.9.0",
"version": "1.9.6",
"private": true,
"main": "./src/index.ts",
"scripts": {
@@ -2,7 +2,6 @@ import {FunctionComponent, useCallback, useState} from 'react';
import {RichTextPlugin} from '@lexical/react/LexicalRichTextPlugin';
import {ContentEditable} from '@lexical/react/LexicalContentEditable';
import {OnChangePlugin} from '@lexical/react/LexicalOnChangePlugin';
import {AutoFocusPlugin} from '@lexical/react/LexicalAutoFocusPlugin';
import {CheckListPlugin} from '@lexical/react/LexicalCheckListPlugin';
import {ClearEditorPlugin} from '@lexical/react/LexicalClearEditorPlugin';
import {MarkdownShortcutPlugin} from '@lexical/react/LexicalMarkdownShortcutPlugin';
@@ -25,7 +24,6 @@ import FloatingLinkEditorPlugin from '../Lexical/Plugins/FloatingLinkEditorPlugi
import {truncateString} from './Utils';
import {SuperEditorContentId} from './Constants';
import {classNames} from '@standardnotes/utils';
import {EditorLineHeight} from '@standardnotes/snjs';
import {MarkdownTransformers} from './MarkdownTransformers';
type BlocksEditorProps = {
@@ -35,7 +33,6 @@ type BlocksEditorProps = {
previewLength?: number;
spellcheck?: boolean;
ignoreFirstChange?: boolean;
lineHeight?: EditorLineHeight;
readonly?: boolean;
};
@@ -46,7 +43,6 @@ export const BlocksEditor: FunctionComponent<BlocksEditorProps> = ({
previewLength,
spellcheck,
ignoreFirstChange = false,
lineHeight,
readonly,
}) => {
const [didIgnoreFirstChange, setDidIgnoreFirstChange] = useState(false);
@@ -104,7 +100,6 @@ export const BlocksEditor: FunctionComponent<BlocksEditorProps> = ({
id={SuperEditorContentId}
className={classNames(
'ContentEditable__root overflow-y-auto',
lineHeight && `leading-${lineHeight.toLowerCase()}`,
className,
)}
spellCheck={spellcheck}
@@ -121,7 +116,6 @@ export const BlocksEditor: FunctionComponent<BlocksEditorProps> = ({
<OnChangePlugin onChange={handleChange} ignoreSelectionChange={true} />
<HistoryPlugin />
<HorizontalRulePlugin />
<AutoFocusPlugin />
<ClearEditorPlugin />
<CheckListPlugin />
<CodeHighlightPlugin />
@@ -17,8 +17,16 @@ import {
DRAGOVER_COMMAND,
DROP_COMMAND,
LexicalEditor,
LexicalNode,
} from 'lexical';
import {DragEvent as ReactDragEvent, useEffect, useRef, useState} from 'react';
import {
DragEvent as ReactDragEvent,
TouchEvent,
useCallback,
useEffect,
useRef,
useState,
} from 'react';
import {createPortal} from 'react-dom';
import {BlockIcon} from '@standardnotes/icons';
@@ -26,7 +34,7 @@ import {isHTMLElement} from '../../Utils/guard';
import {Point} from '../../Utils/point';
import {ContainsPointReturn, Rect} from '../../Utils/rect';
const SPACE = 4;
const DRAGGABLE_BLOCK_MENU_LEFT_SPACE = -2;
const TARGET_LINE_HALF_HEIGHT = 2;
const DRAGGABLE_BLOCK_MENU_CLASSNAME = 'draggable-block-menu';
const DRAG_DATA_FORMAT = 'application/x-lexical-drag-block';
@@ -57,11 +65,10 @@ function getTopLevelNodeKeys(editor: LexicalEditor): string[] {
function elementContainingEventLocation(
anchorElem: HTMLElement,
element: HTMLElement,
event: MouseEvent,
eventLocation: Point,
): {contains: ContainsPointReturn; element: HTMLElement} {
const anchorElementRect = anchorElem.getBoundingClientRect();
const eventLocation = new Point(event.x, event.y);
const elementDomRect = Rect.fromDOM(element);
const {marginTop, marginBottom} = window.getComputedStyle(element);
@@ -87,7 +94,7 @@ function elementContainingEventLocation(
const childResult = elementContainingEventLocation(
anchorElem,
child as HTMLElement,
event,
eventLocation,
);
if (childResult.contains.result) {
@@ -102,7 +109,7 @@ function elementContainingEventLocation(
function getBlockElement(
anchorElem: HTMLElement,
editor: LexicalEditor,
event: MouseEvent,
eventLocation: Point,
): HTMLElement | null {
const topLevelNodeKeys = getTopLevelNodeKeys(editor);
@@ -121,7 +128,7 @@ function getBlockElement(
const {contains, element} = elementContainingEventLocation(
anchorElem,
elem,
event,
eventLocation,
);
if (contains.result) {
@@ -172,7 +179,7 @@ function setMenuPosition(
(parseInt(targetStyle.lineHeight, 10) - floatingElemRect.height) / 2 -
anchorElementRect.top;
const left = SPACE;
const left = DRAGGABLE_BLOCK_MENU_LEFT_SPACE;
floatingElem.style.opacity = '1';
floatingElem.style.transform = `translate(${left}px, ${top}px)`;
@@ -214,11 +221,12 @@ function setTargetLine(
}
const top = lineTop - anchorTop - TARGET_LINE_HALF_HEIGHT;
const left = TEXT_BOX_HORIZONTAL_PADDING - SPACE;
const left = TEXT_BOX_HORIZONTAL_PADDING - DRAGGABLE_BLOCK_MENU_LEFT_SPACE;
targetLineElem.style.transform = `translate(${left}px, ${top}px)`;
targetLineElem.style.width = `${
anchorWidth - (TEXT_BOX_HORIZONTAL_PADDING - SPACE) * 2
anchorWidth -
(TEXT_BOX_HORIZONTAL_PADDING - DRAGGABLE_BLOCK_MENU_LEFT_SPACE) * 2
}px`;
targetLineElem.style.opacity = '.6';
}
@@ -240,6 +248,7 @@ function useDraggableBlockMenu(
const targetLineRef = useRef<HTMLDivElement>(null);
const [draggableBlockElem, setDraggableBlockElem] =
useState<HTMLElement | null>(null);
const dragDataRef = useRef<string | null>(null);
useEffect(() => {
function onMouseMove(event: MouseEvent) {
@@ -253,7 +262,11 @@ function useDraggableBlockMenu(
return;
}
const _draggableBlockElem = getBlockElement(anchorElem, editor, event);
const _draggableBlockElem = getBlockElement(
anchorElem,
editor,
new Point(event.clientX, event.clientY),
);
setDraggableBlockElem(_draggableBlockElem);
}
@@ -277,54 +290,13 @@ function useDraggableBlockMenu(
}
}, [anchorElem, draggableBlockElem]);
useEffect(() => {
function onDragover(event: DragEvent): boolean {
const [isFileTransfer] = eventFiles(event);
if (isFileTransfer) {
return false;
}
const {pageY, target} = event;
if (!isHTMLElement(target)) {
return false;
}
const targetBlockElem = getBlockElement(anchorElem, editor, event);
const targetLineElem = targetLineRef.current;
if (targetBlockElem === null || targetLineElem === null) {
return false;
}
setTargetLine(targetLineElem, targetBlockElem, pageY, anchorElem);
// Prevent default event to be able to trigger onDrop events
event.preventDefault();
return true;
}
function onDrop(event: DragEvent): boolean {
const [isFileTransfer] = eventFiles(event);
if (isFileTransfer) {
return false;
}
const {target, dataTransfer, pageY} = event;
const dragData = dataTransfer?.getData(DRAG_DATA_FORMAT) || '';
const draggedNode = $getNodeByKey(dragData);
if (!draggedNode) {
return false;
}
if (!isHTMLElement(target)) {
return false;
}
const targetBlockElem = getBlockElement(anchorElem, editor, event);
if (!targetBlockElem) {
return false;
}
const targetNode = $getNearestNodeFromDOMNode(targetBlockElem);
if (!targetNode) {
return false;
}
if (targetNode === draggedNode) {
return true;
}
const insertDraggedNode = useCallback(
(
draggedNode: LexicalNode,
targetNode: LexicalNode,
targetBlockElem: HTMLElement,
pageY: number,
) => {
let nodeToInsert = draggedNode;
const targetParent = targetNode.getParent();
const sourceParent = draggedNode.getParent();
@@ -342,6 +314,71 @@ function useDraggableBlockMenu(
} else {
targetNode.insertBefore(nodeToInsert);
}
},
[],
);
useEffect(() => {
function onDragover(event: DragEvent): boolean {
const [isFileTransfer] = eventFiles(event);
if (isFileTransfer) {
return false;
}
const {pageY, target} = event;
if (!isHTMLElement(target)) {
return false;
}
const targetBlockElem = getBlockElement(
anchorElem,
editor,
new Point(event.pageX, pageY),
);
const targetLineElem = targetLineRef.current;
if (targetBlockElem === null || targetLineElem === null) {
return false;
}
setTargetLine(targetLineElem, targetBlockElem, pageY, anchorElem);
// Prevent default event to be able to trigger onDrop events
event.preventDefault();
return true;
}
function onDrop(event: DragEvent): boolean {
const [isFileTransfer] = eventFiles(event);
if (isFileTransfer) {
return false;
}
const {target, dataTransfer, pageY} = event;
if (!isHTMLElement(target)) {
return false;
}
const dragData = dataTransfer?.getData(DRAG_DATA_FORMAT) || '';
const draggedNode = $getNodeByKey(dragData);
if (!draggedNode) {
return false;
}
const targetBlockElem = getBlockElement(
anchorElem,
editor,
new Point(event.pageX, pageY),
);
if (!targetBlockElem) {
return false;
}
const targetNode = $getNearestNodeFromDOMNode(targetBlockElem);
if (!targetNode) {
return false;
}
if (targetNode === draggedNode) {
return true;
}
insertDraggedNode(draggedNode, targetNode, targetBlockElem, event.pageY);
setDraggableBlockElem(null);
return true;
@@ -363,7 +400,7 @@ function useDraggableBlockMenu(
COMMAND_PRIORITY_HIGH,
),
);
}, [anchorElem, editor]);
}, [anchorElem, editor, insertDraggedNode]);
function onDragStart(event: ReactDragEvent<HTMLDivElement>): void {
const dataTransfer = event.dataTransfer;
@@ -385,6 +422,79 @@ function useDraggableBlockMenu(
hideTargetLine(targetLineRef.current);
}
function onTouchStart(): void {
if (!draggableBlockElem) {
return;
}
editor.update(() => {
const node = $getNearestNodeFromDOMNode(draggableBlockElem);
if (!node) {
return;
}
const nodeKey = node.getKey();
dragDataRef.current = nodeKey;
});
}
function onTouchMove(event: TouchEvent) {
const {pageX, pageY} = event.targetTouches[0];
const rootElement = editor.getRootElement();
if (rootElement) {
const {top, bottom} = rootElement.getBoundingClientRect();
const scrollOffset = 20;
if (pageY - top < scrollOffset) {
rootElement.scrollTop -= scrollOffset;
} else if (bottom - pageY < scrollOffset) {
rootElement.scrollTop += scrollOffset;
}
}
const targetBlockElem = getBlockElement(
anchorElem,
editor,
new Point(pageX, pageY),
);
const targetLineElem = targetLineRef.current;
if (targetBlockElem === null || targetLineElem === null) {
return;
}
setTargetLine(targetLineElem, targetBlockElem, pageY, anchorElem);
}
function onTouchEnd(event: TouchEvent): void {
hideTargetLine(targetLineRef.current);
editor.update(() => {
const {pageX, pageY} = event.changedTouches[0];
const dragData = dragDataRef.current || '';
const draggedNode = $getNodeByKey(dragData);
if (!draggedNode) {
return;
}
const targetBlockElem = getBlockElement(
anchorElem,
editor,
new Point(pageX, pageY),
);
if (!targetBlockElem) {
return;
}
const targetNode = $getNearestNodeFromDOMNode(targetBlockElem);
if (!targetNode) {
return;
}
if (targetNode === draggedNode) {
return;
}
insertDraggedNode(draggedNode, targetNode, targetBlockElem, pageY);
});
setDraggableBlockElem(null);
}
return createPortal(
<>
<div
@@ -392,7 +502,10 @@ function useDraggableBlockMenu(
ref={menuRef}
draggable={true}
onDragStart={onDragStart}
onDragEnd={onDragEnd}>
onDragEnd={onDragEnd}
onTouchStart={onTouchStart}
onTouchMove={onTouchMove}
onTouchEnd={onTouchEnd}>
<div className={isEditable ? 'icon' : ''}>
<BlockIcon className="text-text pointer-events-none" />
</div>
@@ -30,12 +30,10 @@
color: var(--sn-stylekit-editor-foreground-color);
font-weight: 700;
margin: 0;
text-transform: uppercase;
}
.Lexical__h3 {
font-size: 12px;
margin: 0;
text-transform: uppercase;
}
.Lexical__textBold {
font-weight: bold;
+124
View File
@@ -3,6 +3,130 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [3.104.10](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-05)
**Note:** Version bump only for package @standardnotes/desktop
## [3.104.9](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-05)
**Note:** Version bump only for package @standardnotes/desktop
## [3.104.8](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-05)
**Note:** Version bump only for package @standardnotes/desktop
## [3.104.7](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-05)
**Note:** Version bump only for package @standardnotes/desktop
## [3.104.6](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-04)
**Note:** Version bump only for package @standardnotes/desktop
## [3.104.5](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-04)
**Note:** Version bump only for package @standardnotes/desktop
## [3.104.4](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-02)
**Note:** Version bump only for package @standardnotes/desktop
## [3.104.3](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-02)
**Note:** Version bump only for package @standardnotes/desktop
## [3.104.2](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-02)
**Note:** Version bump only for package @standardnotes/desktop
## [3.104.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-02)
**Note:** Version bump only for package @standardnotes/desktop
# [3.104.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-01)
### Features
* download and preview files from local backups automatically, if a local backup is available ([#2076](https://github.com/standardnotes/app/issues/2076)) ([28e43d3](https://github.com/standardnotes/app/commit/28e43d37c00c5cd97129c4b83266f7a234bd9857))
## [3.103.2](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-01)
**Note:** Version bump only for package @standardnotes/desktop
## [3.103.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-30)
**Note:** Version bump only for package @standardnotes/desktop
# [3.103.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-30)
### Features
* screen presentation and dismiss animations for mobile ([#2073](https://github.com/standardnotes/app/issues/2073)) ([7f2074a](https://github.com/standardnotes/app/commit/7f2074a6ec59068f75dae76d31721d65e61c9673))
## [3.102.18](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-30)
**Note:** Version bump only for package @standardnotes/desktop
## [3.102.17](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-30)
**Note:** Version bump only for package @standardnotes/desktop
## [3.102.16](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-29)
**Note:** Version bump only for package @standardnotes/desktop
## [3.102.15](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-28)
**Note:** Version bump only for package @standardnotes/desktop
## [3.102.14](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-28)
**Note:** Version bump only for package @standardnotes/desktop
## [3.102.13](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-28)
**Note:** Version bump only for package @standardnotes/desktop
## [3.102.12](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-28)
**Note:** Version bump only for package @standardnotes/desktop
## [3.102.11](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-28)
**Note:** Version bump only for package @standardnotes/desktop
## [3.102.10](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-28)
**Note:** Version bump only for package @standardnotes/desktop
## [3.102.9](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-28)
**Note:** Version bump only for package @standardnotes/desktop
## [3.102.8](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-27)
**Note:** Version bump only for package @standardnotes/desktop
## [3.102.7](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-27)
**Note:** Version bump only for package @standardnotes/desktop
## [3.102.6](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-27)
**Note:** Version bump only for package @standardnotes/desktop
## [3.102.5](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-27)
**Note:** Version bump only for package @standardnotes/desktop
## [3.102.4](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-25)
**Note:** Version bump only for package @standardnotes/desktop
## [3.102.3](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-25)
**Note:** Version bump only for package @standardnotes/desktop
## [3.102.2](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-24)
**Note:** Version bump only for package @standardnotes/desktop
@@ -1,4 +1,10 @@
import { FileBackupRecord, FileBackupsDevice, FileBackupsMapping } from '@web/Application/Device/DesktopSnjsExports'
import {
FileBackupRecord,
FileBackupsDevice,
FileBackupsMapping,
FileBackupReadToken,
FileBackupReadChunkResponse,
} from '@web/Application/Device/DesktopSnjsExports'
import { AppState } from 'app/AppState'
import { shell } from 'electron'
import { StoreKeys } from '../Store/StoreKeys'
@@ -6,13 +12,14 @@ import path from 'path'
import {
deleteFile,
ensureDirectoryExists,
moveFiles,
moveDirContents,
openDirectoryPicker,
readJSONFile,
writeFile,
writeJSONFile,
} from '../Utils/FileUtils'
import { FileDownloader } from './FileDownloader'
import { FileReadOperation } from './FileReadOperation'
export const FileBackupsConstantsV1 = {
Version: '1.0.0',
@@ -21,6 +28,8 @@ export const FileBackupsConstantsV1 = {
}
export class FilesBackupManager implements FileBackupsDevice {
private readOperations: Map<string, FileReadOperation> = new Map()
constructor(private appState: AppState) {}
public isFilesBackupsEnabled(): Promise<boolean> {
@@ -78,8 +87,11 @@ export class FilesBackupManager implements FileBackupsDevice {
}
const entries = Object.values(mapping.files)
const itemFolders = entries.map((entry) => path.join(oldPath, entry.relativePath))
await moveFiles(itemFolders, newPath)
for (const entry of entries) {
const sourcePath = path.join(oldPath, entry.relativePath)
const destinationPath = path.join(newPath, entry.relativePath)
await moveDirContents(sourcePath, destinationPath)
}
for (const entry of entries) {
entry.absolutePath = path.join(newPath, entry.relativePath)
@@ -188,4 +200,28 @@ export class FilesBackupManager implements FileBackupsDevice {
return result
}
async getFileBackupReadToken(record: FileBackupRecord): Promise<FileBackupReadToken> {
const operation = new FileReadOperation(record)
this.readOperations.set(operation.token, operation)
return operation.token
}
async readNextChunk(token: string): Promise<FileBackupReadChunkResponse> {
const operation = this.readOperations.get(token)
if (!operation) {
return Promise.reject(new Error('Invalid token'))
}
const result = await operation.readNextChunk()
if (result.isLast) {
this.readOperations.delete(token)
}
return result
}
}
@@ -0,0 +1,58 @@
import { FileBackupReadChunkResponse, FileBackupRecord } from '@web/Application/Device/DesktopSnjsExports'
import fs from 'fs'
import path from 'path'
const ONE_MB = 1024 * 1024
const CHUNK_LIMIT = ONE_MB * 5
export class FileReadOperation {
public readonly token: string
private currentChunkLocation = 0
private localFileId: number
private fileLength: number
constructor(backupRecord: FileBackupRecord) {
this.token = backupRecord.absolutePath
this.localFileId = fs.openSync(path.join(backupRecord.absolutePath, backupRecord.binaryFileName), 'r')
this.fileLength = fs.fstatSync(this.localFileId).size
}
async readNextChunk(): Promise<FileBackupReadChunkResponse> {
let isLast = false
let readUpto = this.currentChunkLocation + CHUNK_LIMIT
if (readUpto > this.fileLength) {
readUpto = this.fileLength
isLast = true
}
const readLength = readUpto - this.currentChunkLocation
const chunk = await this.readChunk(this.currentChunkLocation, readLength)
this.currentChunkLocation = readUpto
if (isLast) {
fs.close(this.localFileId)
}
return {
chunk,
isLast,
progress: {
encryptedFileSize: this.fileLength,
encryptedBytesDownloaded: this.currentChunkLocation,
encryptedBytesRemaining: this.fileLength - this.currentChunkLocation,
percentComplete: (this.currentChunkLocation / this.fileLength) * 100.0,
source: 'local',
},
}
}
async readChunk(start: number, length: number): Promise<Uint8Array> {
const buffer = Buffer.alloc(length)
fs.readSync(this.localFileId, buffer, 0, length, start)
return buffer
}
}
@@ -0,0 +1,18 @@
import { MediaManagerInterface } from './MediaManagerInterface'
const { systemPreferences } = require('electron')
export class MediaManager implements MediaManagerInterface {
async askForMediaAccess(type: 'camera' | 'microphone'): Promise<boolean> {
const permission = systemPreferences.getMediaAccessStatus(type)
if (permission === 'granted') {
return true
} else if (permission === 'denied') {
return false
}
const granted = await systemPreferences.askForMediaAccess(type)
return granted
}
}
@@ -0,0 +1,3 @@
export interface MediaManagerInterface {
askForMediaAccess(type: 'camera' | 'microphone'): Promise<boolean>
}
@@ -5,7 +5,13 @@ import { StoreKeys } from '../Store/StoreKeys'
const path = require('path')
const rendererPath = path.join('file://', __dirname, '/renderer.js')
import { FileBackupsDevice, FileBackupsMapping, FileBackupRecord } from '@web/Application/Device/DesktopSnjsExports'
import {
FileBackupsDevice,
FileBackupsMapping,
FileBackupRecord,
FileBackupReadToken,
FileBackupReadChunkResponse,
} from '@web/Application/Device/DesktopSnjsExports'
import { app, BrowserWindow } from 'electron'
import { BackupsManagerInterface } from '../Backups/BackupsManagerInterface'
import { KeychainInterface } from '../Keychain/KeychainInterface'
@@ -13,6 +19,7 @@ import { MenuManagerInterface } from '../Menus/MenuManagerInterface'
import { Component, PackageManagerInterface } from '../Packages/PackageManagerInterface'
import { SearchManagerInterface } from '../Search/SearchManagerInterface'
import { RemoteDataInterface } from './DataInterface'
import { MediaManagerInterface } from '../Media/MediaManagerInterface'
/**
* Read https://github.com/electron/remote to understand how electron/remote works.
@@ -28,6 +35,7 @@ export class RemoteBridge implements CrossProcessBridge {
private data: RemoteDataInterface,
private menus: MenuManagerInterface,
private fileBackups: FileBackupsDevice,
private media: MediaManagerInterface,
) {}
get exposableValue(): CrossProcessBridge {
@@ -65,6 +73,9 @@ export class RemoteBridge implements CrossProcessBridge {
getFilesBackupsLocation: this.getFilesBackupsLocation.bind(this),
openFilesBackupsLocation: this.openFilesBackupsLocation.bind(this),
openFileBackup: this.openFileBackup.bind(this),
getFileBackupReadToken: this.getFileBackupReadToken.bind(this),
readNextChunk: this.readNextChunk.bind(this),
askForMediaAccess: this.askForMediaAccess.bind(this),
}
}
@@ -180,6 +191,14 @@ export class RemoteBridge implements CrossProcessBridge {
return this.fileBackups.saveFilesBackupsFile(uuid, metaFile, downloadRequest)
}
getFileBackupReadToken(record: FileBackupRecord): Promise<FileBackupReadToken> {
return this.fileBackups.getFileBackupReadToken(record)
}
readNextChunk(nextToken: string): Promise<FileBackupReadChunkResponse> {
return this.fileBackups.readNextChunk(nextToken)
}
public isFilesBackupsEnabled(): Promise<boolean> {
return this.fileBackups.isFilesBackupsEnabled()
}
@@ -207,4 +226,8 @@ export class RemoteBridge implements CrossProcessBridge {
public openFileBackup(record: FileBackupRecord): Promise<void> {
return this.fileBackups.openFileBackup(record)
}
askForMediaAccess(type: 'camera' | 'microphone'): Promise<boolean> {
return this.media.askForMediaAccess(type)
}
}
@@ -7,7 +7,6 @@ import { removeFromArray } from '../Utils/Utils'
export const FileDoesNotExist = 'ENOENT'
export const FileAlreadyExists = 'EEXIST'
const CrossDeviceLink = 'EXDEV'
const OperationNotPermitted = 'EPERM'
const DeviceIsBusy = 'EBUSY'
@@ -152,8 +151,14 @@ function isChildOfDir(parent: string, potentialChild: string) {
return relative && !relative.startsWith('..') && !path.isAbsolute(relative)
}
export async function moveDirContents(srcDir: string, destDir: string): Promise<void[]> {
let fileNames = await fs.promises.readdir(srcDir)
export async function moveDirContents(srcDir: string, destDir: string): Promise<void> {
let fileNames: string[]
try {
fileNames = await fs.promises.readdir(srcDir)
} catch (error) {
console.error(error)
return
}
await ensureDirectoryExists(destDir)
if (isChildOfDir(srcDir, destDir)) {
@@ -163,10 +168,14 @@ export async function moveDirContents(srcDir: string, destDir: string): Promise<
removeFromArray(fileNames, path.basename(destDir))
}
return moveFiles(
fileNames.map((fileName) => path.join(srcDir, fileName)),
destDir,
)
try {
await moveFiles(
fileNames.map((fileName) => path.join(srcDir, fileName)),
destDir,
)
} catch (error) {
console.error(error)
}
}
export async function extractZip(source: string, dest: string): Promise<void> {
@@ -245,14 +254,10 @@ export async function moveFiles(sources: string[], destDir: string): Promise<voi
async function moveFile(source: PathLike, destination: PathLike) {
try {
await fs.promises.rename(source, destination)
} catch (error: any) {
if (error.code === CrossDeviceLink) {
/** Fall back to copying and then deleting. */
await fs.promises.copyFile(source, destination)
await fs.promises.unlink(source)
} else {
throw error
}
} catch (_error) {
/** Fall back to copying and then deleting. */
await fs.promises.copyFile(source, destination, fs.constants.COPYFILE_FICLONE_FORCE)
await fs.promises.unlink(source)
}
}
@@ -10,6 +10,7 @@ import { createBackupsManager } from './Backups/BackupsManager'
import { BackupsManagerInterface } from './Backups/BackupsManagerInterface'
import { FilesBackupManager } from './FileBackups/FileBackupsManager'
import { Keychain } from './Keychain/Keychain'
import { MediaManager } from './Media/MediaManager'
import { MenuManagerInterface } from './Menus/MenuManagerInterface'
import { buildContextMenu, createMenuManager } from './Menus/Menus'
import { initializePackageManager } from './Packages/PackageManager'
@@ -74,6 +75,7 @@ export async function createWindowState({
},
services.menuManager,
services.fileBackupsManager,
services.mediaManager,
)
const shouldOpenUrl = (url: string) => url.startsWith('http') || url.startsWith('mailto')
@@ -203,6 +205,7 @@ async function createWindowServices(window: Electron.BrowserWindow, appState: Ap
const updateManager = setupUpdates(window, appState, backupsManager)
const trayManager = createTrayManager(window, appState.store)
const spellcheckerManager = createSpellcheckerManager(appState.store, window.webContents, appLocale)
const mediaManager = new MediaManager()
if (isTesting()) {
handleTestMessage(MessageType.SpellCheckerManager, () => spellcheckerManager)
@@ -228,6 +231,7 @@ async function createWindowServices(window: Electron.BrowserWindow, appState: Ap
packageManager,
searchManager,
fileBackupsManager,
mediaManager,
}
}
@@ -49,4 +49,6 @@ export interface CrossProcessBridge extends FileBackupsDevice {
onInitialDataLoad(): void
destroyAllData(): void
askForMediaAccess(type: 'camera' | 'microphone'): Promise<boolean>
}
@@ -4,6 +4,8 @@ import {
FileBackupsMapping,
RawKeychainValue,
FileBackupRecord,
FileBackupReadToken,
FileBackupReadChunkResponse,
} from '@web/Application/Device/DesktopSnjsExports'
import { WebOrDesktopDevice } from '@web/Application/Device/WebOrDesktopDevice'
import { Component } from '../Main/Packages/PackageManagerInterface'
@@ -149,6 +151,14 @@ export class DesktopDevice extends WebOrDesktopDevice implements DesktopDeviceIn
return this.remoteBridge.saveFilesBackupsFile(uuid, metaFile, downloadRequest)
}
getFileBackupReadToken(record: FileBackupRecord): Promise<FileBackupReadToken> {
return this.remoteBridge.getFileBackupReadToken(record)
}
readNextChunk(token: string): Promise<FileBackupReadChunkResponse> {
return this.remoteBridge.readNextChunk(token)
}
async performHardReset(): Promise<void> {
console.error('performHardReset is not yet implemented')
}
@@ -156,4 +166,8 @@ export class DesktopDevice extends WebOrDesktopDevice implements DesktopDeviceIn
isDeviceDestroyed(): boolean {
return false
}
askForMediaAccess(type: 'camera' | 'microphone'): Promise<boolean> {
return this.remoteBridge.askForMediaAccess(type)
}
}
@@ -22,7 +22,7 @@ declare global {
plansUrl: string
purchaseUrl: string
startApplication: StartApplication
zip: any
zip: unknown
electronMainEvents: any
}
}
@@ -88,15 +88,15 @@ async function configureWindow(remoteBridge: CrossProcessBridge) {
/*
Title bar events
*/
document.getElementById('menu-btn')!.addEventListener('click', () => {
document.getElementById('menu-btn')?.addEventListener('click', () => {
remoteBridge.displayAppMenu()
})
document.getElementById('min-btn')!.addEventListener('click', () => {
document.getElementById('min-btn')?.addEventListener('click', () => {
remoteBridge.minimizeWindow()
})
document.getElementById('max-btn')!.addEventListener('click', async () => {
document.getElementById('max-btn')?.addEventListener('click', async () => {
if (remoteBridge.isWindowMaximized()) {
remoteBridge.unmaximizeWindow()
} else {
@@ -104,15 +104,12 @@ async function configureWindow(remoteBridge: CrossProcessBridge) {
}
})
document.getElementById('close-btn')!.addEventListener('click', () => {
document.getElementById('close-btn')?.addEventListener('click', () => {
remoteBridge.closeWindow()
})
// For Mac inset window
const sheet = document.styleSheets[0]
if (isMacOS) {
sheet.insertRule('#navigation-content { padding-top: 25px !important; }', sheet.cssRules.length)
}
if (isMacOS || useSystemMenuBar) {
// !important is important here because #desktop-title-bar has display: flex.
@@ -141,7 +138,7 @@ window.electronMainEvents.handlePerformAutomatedBackup(() => {
void window.device.downloadBackup()
})
window.electronMainEvents.handleFinishedSavingBackup((_: IpcRendererEvent, data: any) => {
window.electronMainEvents.handleFinishedSavingBackup((_: IpcRendererEvent, data: { success: boolean }) => {
window.webClient.didFinishBackup(data.success)
})
@@ -10,18 +10,14 @@
}
@media screen and (max-width: 768px) {
.mac-desktop .app-column.selected {
padding-top: 18px;
.mac-desktop .app-pane {
padding-top: 25px;
}
}
@media screen and (min-width: 768px) {
.mac-desktop #app.collapsed-notes.collapsed-navigation #editor-column {
padding-top: 18px;
}
.mac-desktop #app.collapsed-navigation #items-column {
padding-top: 18px;
.mac-desktop .app-pane-1 {
padding-top: 25px;
}
}
@@ -10,5 +10,7 @@
<true/>
<key>com.apple.security.cs.disable-library-validation</key>
<true/>
<key>com.apple.security.device.camera</key>
<true/>
</dict>
</plist>
+4 -1
View File
@@ -1,7 +1,7 @@
{
"name": "@standardnotes/desktop",
"main": "./app/dist/index.js",
"version": "3.102.2",
"version": "3.104.10",
"license": "AGPL-3.0-or-later",
"author": "Standard Notes.",
"private": true,
@@ -104,6 +104,9 @@
"hardenedRuntime": true,
"entitlements": "./build/entitlements.mac.inherit.plist",
"entitlementsInherit": "./build/entitlements.mac.inherit.plist",
"extendInfo": {
"NSCameraUsageDescription": "Standard Notes requires access to your camera to enable the Moments feature."
},
"target": [
"dmg",
"zip"
+32
View File
@@ -3,6 +3,38 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.19.26](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-05)
**Note:** Version bump only for package @standardnotes/encryption
## [1.19.25](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-02)
**Note:** Version bump only for package @standardnotes/encryption
## [1.19.24](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-02)
**Note:** Version bump only for package @standardnotes/encryption
## [1.19.23](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-01)
**Note:** Version bump only for package @standardnotes/encryption
## [1.19.22](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-01)
**Note:** Version bump only for package @standardnotes/encryption
## [1.19.21](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-28)
**Note:** Version bump only for package @standardnotes/encryption
## [1.19.20](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-28)
**Note:** Version bump only for package @standardnotes/encryption
## [1.19.19](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-27)
**Note:** Version bump only for package @standardnotes/encryption
## [1.19.18](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-23)
**Note:** Version bump only for package @standardnotes/encryption
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/encryption",
"version": "1.19.18",
"version": "1.19.26",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
+6
View File
@@ -3,6 +3,12 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [1.56.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-05)
### Features
* add Super note type to list of note types ([#2086](https://github.com/standardnotes/app/issues/2086)) ([caf2c4a](https://github.com/standardnotes/app/commit/caf2c4a87646ab4d1aa7007c39547fd3b16c0c6f))
## [1.55.3](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-17)
### Bug Fixes
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/features",
"version": "1.55.3",
"version": "1.56.0",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
@@ -51,4 +51,4 @@ export enum FeatureIdentifier {
*/
export const LegacyFileSafeIdentifier = 'org.standardnotes.legacy.file-safe'
export const ExperimentalFeatures = [FeatureIdentifier.SuperEditor]
export const ExperimentalFeatures = []
@@ -1,7 +1,7 @@
import { ClientFeatureDescription } from '../Feature/FeatureDescription'
import { PermissionName } from '../Permission/PermissionName'
import { FeatureIdentifier } from '../Feature/FeatureIdentifier'
import { SubscriptionName } from '@standardnotes/common'
import { RoleName, SubscriptionName } from '@standardnotes/common'
export function clientFeatures(): ClientFeatureDescription[] {
return [
@@ -12,6 +12,15 @@ export function clientFeatures(): ClientFeatureDescription[] {
permission_name: PermissionName.TagNesting,
description: 'Organize your tags into folders.',
},
{
name: 'Super Notes',
identifier: FeatureIdentifier.SuperEditor,
availableInSubscriptions: [SubscriptionName.PlusPlan, SubscriptionName.ProPlan],
permission_name: PermissionName.SuperEditor,
description:
'Type / to bring up the block selection menu, or @ to embed images or link other tags and notes. Type - then space to start a list, or [] then space to start a checklist. Drag and drop an image or file to embed it in your note.',
availableInRoles: [RoleName.PlusUser, RoleName.ProUser],
},
{
availableInSubscriptions: [SubscriptionName.PlusPlan, SubscriptionName.ProPlan],
name: 'Smart Filters',
@@ -1,18 +1,5 @@
import { FeatureIdentifier } from './../Feature/FeatureIdentifier'
import { RoleName, SubscriptionName } from '@standardnotes/common'
import { FeatureDescription } from '../Feature/FeatureDescription'
import { PermissionName } from '../Permission/PermissionName'
export function experimentalFeatures(): FeatureDescription[] {
const superEditor: FeatureDescription = {
name: 'Super Notes',
identifier: FeatureIdentifier.SuperEditor,
availableInSubscriptions: [SubscriptionName.PlusPlan, SubscriptionName.ProPlan],
permission_name: PermissionName.SuperEditor,
description:
'A new way to edit notes. Type / to bring up the block selection menu, or @ to embed images or link other tags and notes. Type - then space to start a list, or [] then space to start a checklist. Drag and drop an image or file to embed it in your note.',
availableInRoles: [RoleName.PlusUser, RoleName.ProUser],
}
return [superEditor]
return []
}
+36
View File
@@ -3,6 +3,42 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.27.3](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-05)
**Note:** Version bump only for package @standardnotes/filepicker
## [1.27.2](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-02)
### Bug Fixes
* **mobile:** Fixes issue where file upload doesn't work with immediate biometrics ([#2083](https://github.com/standardnotes/app/issues/2083)) ([6cacdd8](https://github.com/standardnotes/app/commit/6cacdd8c5882ac562c73a706aa4237c53a049f5c))
## [1.27.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-02)
**Note:** Version bump only for package @standardnotes/filepicker
# [1.27.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-01)
### Features
* download and preview files from local backups automatically, if a local backup is available ([#2076](https://github.com/standardnotes/app/issues/2076)) ([28e43d3](https://github.com/standardnotes/app/commit/28e43d37c00c5cd97129c4b83266f7a234bd9857))
## [1.26.5](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-01)
**Note:** Version bump only for package @standardnotes/filepicker
## [1.26.4](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-28)
**Note:** Version bump only for package @standardnotes/filepicker
## [1.26.3](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-28)
**Note:** Version bump only for package @standardnotes/filepicker
## [1.26.2](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-27)
**Note:** Version bump only for package @standardnotes/filepicker
## [1.26.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-23)
**Note:** Version bump only for package @standardnotes/filepicker
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/filepicker",
"version": "1.26.1",
"version": "1.27.3",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
@@ -1,4 +1,4 @@
import { ByteChunker, OnChunkCallback } from '@standardnotes/files'
import { ByteChunker, OnChunkCallbackNoProgress } from '@standardnotes/files'
import { FileSelectionResponse } from '../types'
import { readFile as utilsReadFile } from '../utils'
import { FileReaderInterface } from '../Interface/FileReader'
@@ -18,16 +18,31 @@ function maximumFileSize(): number {
return 50 * 1_000_000
}
const FileInputId = 'classic-reader-file-input'
function createFileInputOrReturnExisting(): HTMLInputElement {
let fileInput = document.getElementById(FileInputId) as HTMLInputElement
if (fileInput) {
return fileInput
}
fileInput = document.createElement('input')
fileInput.id = FileInputId
fileInput.type = 'file'
fileInput.className = 'absolute top-0 left-0 -z-50 h-px w-px opacity-0'
fileInput.multiple = true
document.body.appendChild(fileInput)
return fileInput
}
function selectFiles(): Promise<File[]> {
const input = document.createElement('input') as HTMLInputElement
input.type = 'file'
input.multiple = true
const input = createFileInputOrReturnExisting()
return new Promise((resolve) => {
input.onchange = async (event) => {
const target = event.target as HTMLInputElement
const files = []
for (const file of target.files as FileList) {
for (const file of Array.from(target.files as FileList)) {
files.push(file)
}
resolve(files)
@@ -39,7 +54,7 @@ function selectFiles(): Promise<File[]> {
async function readFile(
file: File,
minimumChunkSize: number,
onChunk: OnChunkCallback,
onChunk: OnChunkCallbackNoProgress,
): Promise<FileSelectionResponse> {
const buffer = await utilsReadFile(file)
const chunker = new ByteChunker(minimumChunkSize, onChunk)
@@ -1,11 +1,11 @@
import { OnChunkCallback } from '@standardnotes/files'
import { OnChunkCallbackNoProgress } from '@standardnotes/files'
import { FileSelectionResponse } from '../types'
export interface FileReaderInterface {
selectFiles(): Promise<File[]>
readFile(file: File, minimumChunkSize: number, onChunk: OnChunkCallback): Promise<FileSelectionResponse>
readFile(file: File, minimumChunkSize: number, onChunk: OnChunkCallbackNoProgress): Promise<FileSelectionResponse>
available(): boolean
@@ -1,4 +1,4 @@
import { ByteChunker, OnChunkCallback } from '@standardnotes/files'
import { ByteChunker, OnChunkCallbackNoProgress } from '@standardnotes/files'
import { FileReaderInterface } from './../Interface/FileReader'
import { FileSelectionResponse } from '../types'
@@ -39,7 +39,7 @@ async function selectFiles(): Promise<File[]> {
async function readFile(
file: File,
minimumChunkSize: number,
onChunk: OnChunkCallback,
onChunk: OnChunkCallbackNoProgress,
): Promise<FileSelectionResponse> {
const byteChunker = new ByteChunker(minimumChunkSize, onChunk)
const stream = file.stream() as unknown as ReadableStream
+34
View File
@@ -3,6 +3,40 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.14.3](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-05)
**Note:** Version bump only for package @standardnotes/files
## [1.14.2](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-02)
**Note:** Version bump only for package @standardnotes/files
## [1.14.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-02)
**Note:** Version bump only for package @standardnotes/files
# [1.14.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-01)
### Features
* download and preview files from local backups automatically, if a local backup is available ([#2076](https://github.com/standardnotes/app/issues/2076)) ([28e43d3](https://github.com/standardnotes/app/commit/28e43d37c00c5cd97129c4b83266f7a234bd9857))
## [1.13.5](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-01)
**Note:** Version bump only for package @standardnotes/files
## [1.13.4](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-28)
**Note:** Version bump only for package @standardnotes/files
## [1.13.3](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-28)
**Note:** Version bump only for package @standardnotes/files
## [1.13.2](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-27)
**Note:** Version bump only for package @standardnotes/files
## [1.13.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-23)
**Note:** Version bump only for package @standardnotes/files
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/files",
"version": "1.13.1",
"version": "1.14.3",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
@@ -8,9 +8,9 @@ describe('byte chunker', () => {
it('should hold back small chunks until minimum size is met', async () => {
let receivedBytes = new Uint8Array()
let numChunks = 0
const chunker = new ByteChunker(100, async (bytes) => {
const chunker = new ByteChunker(100, async (chunk) => {
numChunks++
receivedBytes = new Uint8Array([...receivedBytes, ...bytes])
receivedBytes = new Uint8Array([...receivedBytes, ...chunk.data])
})
await chunker.addBytes(chunkOfSize(50), false)
@@ -25,9 +25,9 @@ describe('byte chunker', () => {
it('should send back big chunks immediately', async () => {
let receivedBytes = new Uint8Array()
let numChunks = 0
const chunker = new ByteChunker(100, async (bytes) => {
const chunker = new ByteChunker(100, async (chunk) => {
numChunks++
receivedBytes = new Uint8Array([...receivedBytes, ...bytes])
receivedBytes = new Uint8Array([...receivedBytes, ...chunk.data])
})
await chunker.addBytes(chunkOfSize(150), false)
@@ -42,9 +42,9 @@ describe('byte chunker', () => {
it('last chunk should be popped regardless of size', async () => {
let receivedBytes = new Uint8Array()
let numChunks = 0
const chunker = new ByteChunker(100, async (bytes) => {
const chunker = new ByteChunker(100, async (chunk) => {
numChunks++
receivedBytes = new Uint8Array([...receivedBytes, ...bytes])
receivedBytes = new Uint8Array([...receivedBytes, ...chunk.data])
})
await chunker.addBytes(chunkOfSize(50), false)
@@ -57,9 +57,9 @@ describe('byte chunker', () => {
it('single chunk should be popped immediately', async () => {
let receivedBytes = new Uint8Array()
let numChunks = 0
const chunker = new ByteChunker(100, async (bytes) => {
const chunker = new ByteChunker(100, async (chunk) => {
numChunks++
receivedBytes = new Uint8Array([...receivedBytes, ...bytes])
receivedBytes = new Uint8Array([...receivedBytes, ...chunk.data])
})
await chunker.addBytes(chunkOfSize(50), true)
@@ -1,11 +1,11 @@
import { OnChunkCallback } from './OnChunkCallback'
import { OnChunkCallbackNoProgress } from './OnChunkCallback'
export class ByteChunker {
public loggingEnabled = false
private bytes = new Uint8Array()
private index = 1
constructor(private minimumChunkSize: number, private onChunk: OnChunkCallback) {}
constructor(private minimumChunkSize: number, private onChunk: OnChunkCallbackNoProgress) {}
private log(...args: any[]): void {
if (!this.loggingEnabled) {
@@ -27,9 +27,13 @@ export class ByteChunker {
private async popBytes(isLast: boolean): Promise<void> {
const maxIndex = Math.max(this.minimumChunkSize, this.bytes.length)
const chunk = this.bytes.slice(0, maxIndex)
this.bytes = new Uint8Array([...this.bytes.slice(maxIndex)])
this.log(`Chunker popping ${chunk.length}, total size in queue ${this.bytes.length}`)
await this.onChunk(chunk, this.index++, isLast)
await this.onChunk({ data: chunk, index: this.index++, isLast })
}
}
@@ -1 +1,10 @@
export type OnChunkCallback = (chunk: Uint8Array, index: number, isLast: boolean) => Promise<void>
import { FileDownloadProgress } from '../Types/FileDownloadProgress'
export type OnChunkCallback = (chunk: {
data: Uint8Array
index: number
isLast: boolean
progress: FileDownloadProgress
}) => Promise<void>
export type OnChunkCallbackNoProgress = (chunk: { data: Uint8Array; index: number; isLast: boolean }) => Promise<void>
@@ -10,9 +10,30 @@ describe('ordered byte chunker', () => {
let receivedBytes = new Uint8Array()
let numCallbacks = 0
const chunker = new OrderedByteChunker(chunkSizes, async (bytes) => {
const chunker = new OrderedByteChunker(chunkSizes, 'network', async (chunk) => {
numCallbacks++
receivedBytes = new Uint8Array([...receivedBytes, ...bytes])
receivedBytes = new Uint8Array([...receivedBytes, ...chunk.data])
})
await chunker.addBytes(chunkOfSize(30))
expect(numCallbacks).toEqual(3)
expect(receivedBytes.length).toEqual(30)
})
it('should correctly report progress', async () => {
const chunkSizes = [10, 10, 10]
let receivedBytes = new Uint8Array()
let numCallbacks = 0
const chunker = new OrderedByteChunker(chunkSizes, 'network', async (chunk) => {
numCallbacks++
receivedBytes = new Uint8Array([...receivedBytes, ...chunk.data])
expect(chunk.progress.encryptedBytesDownloaded).toEqual(receivedBytes.length)
expect(chunk.progress.percentComplete).toEqual((numCallbacks / chunkSizes.length) * 100.0)
})
await chunker.addBytes(chunkOfSize(30))
@@ -1,13 +1,28 @@
import { FileDownloadProgress } from '../Types/FileDownloadProgress'
import { OnChunkCallback } from './OnChunkCallback'
export class OrderedByteChunker {
private bytes = new Uint8Array()
private index = 1
private remainingChunks: number[] = []
private fileSize: number
constructor(
private chunkSizes: number[],
private onChunk: (chunk: Uint8Array, index: number, isLast: boolean) => Promise<void>,
private source: FileDownloadProgress['source'],
private onChunk: OnChunkCallback,
) {
this.remainingChunks = chunkSizes.slice()
this.fileSize = chunkSizes.reduce((acc, size) => acc + size, 0)
}
private get bytesPopped(): number {
return this.fileSize - this.bytesRemaining
}
private get bytesRemaining(): number {
return this.remainingChunks.reduce((acc, size) => acc + size, 0)
}
private needsPop(): boolean {
@@ -31,7 +46,18 @@ export class OrderedByteChunker {
this.remainingChunks.shift()
await this.onChunk(chunk, this.index++, this.index === this.chunkSizes.length - 1)
await this.onChunk({
data: chunk,
index: this.index++,
isLast: this.index === this.chunkSizes.length - 1,
progress: {
encryptedFileSize: this.fileSize,
encryptedBytesDownloaded: this.bytesPopped,
encryptedBytesRemaining: this.bytesRemaining,
percentComplete: (this.bytesPopped / this.fileSize) * 100.0,
source: this.source,
},
})
if (this.needsPop()) {
await this.popBytes()
@@ -1,6 +1,10 @@
import { Uuid } from '@standardnotes/common'
import { FileDownloadProgress } from '../Types/FileDownloadProgress'
import { FileBackupRecord, FileBackupsMapping } from './FileBackupsMapping'
export type FileBackupReadToken = string
export type FileBackupReadChunkResponse = { chunk: Uint8Array; isLast: boolean; progress: FileDownloadProgress }
export interface FileBackupsDevice {
getFilesBackupsMappingFile(): Promise<FileBackupsMapping>
saveFilesBackupsFile(
@@ -12,6 +16,8 @@ export interface FileBackupsDevice {
url: string
},
): Promise<'success' | 'failed'>
getFileBackupReadToken(record: FileBackupRecord): Promise<FileBackupReadToken>
readNextChunk(token: string): Promise<FileBackupReadChunkResponse>
isFilesBackupsEnabled(): Promise<boolean>
enableFilesBackups(): Promise<void>
disableFilesBackups(): Promise<void>
+30
View File
@@ -0,0 +1,30 @@
import { logWithColor } from '@standardnotes/utils'
declare const process: {
env: {
NODE_ENV: string | null | undefined
}
}
export const isDev = process.env.NODE_ENV === 'development' || process.env.NODE_ENV === 'test'
export enum LoggingDomain {
FilesPackage,
}
const LoggingStatus: Record<LoggingDomain, boolean> = {
[LoggingDomain.FilesPackage]: false,
}
const DomainColor: Record<LoggingDomain, string> = {
[LoggingDomain.FilesPackage]: 'green',
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export function log(domain: LoggingDomain, ...args: any[]): void {
if (!isDev || !LoggingStatus[domain]) {
return
}
logWithColor(LoggingDomain[domain], DomainColor[domain], ...args)
}
@@ -0,0 +1,14 @@
import { OnChunkCallback } from '../Chunker/OnChunkCallback'
import { FileBackupRecord } from '../Device/FileBackupsMapping'
export interface BackupServiceInterface {
getFileBackupInfo(file: { uuid: string }): Promise<FileBackupRecord | undefined>
readEncryptedFileFromBackup(uuid: string, onChunk: OnChunkCallback): Promise<'success' | 'failed' | 'aborted'>
isFilesBackupsEnabled(): Promise<boolean>
enableFilesBackups(): Promise<void>
disableFilesBackups(): Promise<void>
changeFilesBackupsLocation(): Promise<string | undefined>
getFilesBackupsLocation(): Promise<string>
openFilesBackupsLocation(): Promise<void>
openFileBackup(record: FileBackupRecord): Promise<void>
}
@@ -24,7 +24,7 @@ export interface FilesClientInterface {
downloadFile(
file: FileItem,
onDecryptedBytes: (bytes: Uint8Array, progress: FileDownloadProgress | undefined) => Promise<void>,
onDecryptedBytes: (bytes: Uint8Array, progress: FileDownloadProgress) => Promise<void>,
): Promise<ClientDisplayableError | undefined>
deleteFile(file: FileItem): Promise<ClientDisplayableError | undefined>
@@ -5,7 +5,7 @@ import { FileSystemApi } from '../Api/FileSystemApi'
import { FileHandleRead } from '../Api/FileHandleRead'
import { OrderedByteChunker } from '../Chunker/OrderedByteChunker'
export async function readAndDecryptBackupFile(
export async function readAndDecryptBackupFileUsingFileSystemAPI(
fileHandle: FileHandleRead,
file: {
encryptionHeader: FileContent['encryptionHeader']
@@ -19,8 +19,8 @@ export async function readAndDecryptBackupFile(
): Promise<'aborted' | 'failed' | 'success'> {
const decryptor = new FileDecryptor(file, crypto)
const byteChunker = new OrderedByteChunker(file.encryptedChunkSizes, async (chunk: Uint8Array) => {
const decryptResult = decryptor.decryptBytes(chunk)
const byteChunker = new OrderedByteChunker(file.encryptedChunkSizes, 'local', async (chunk) => {
const decryptResult = decryptor.decryptBytes(chunk.data)
if (!decryptResult) {
return
@@ -0,0 +1,52 @@
import { FileContent } from '@standardnotes/models'
import { PureCryptoInterface } from '@standardnotes/sncrypto-common'
import { FileDecryptor } from '../UseCase/FileDecryptor'
import { OrderedByteChunker } from '../Chunker/OrderedByteChunker'
import { BackupServiceInterface } from './BackupServiceInterface'
import { OnChunkCallback } from '../Chunker/OnChunkCallback'
import { log, LoggingDomain } from '../Logging'
export async function readAndDecryptBackupFileUsingBackupService(
file: {
uuid: string
encryptionHeader: FileContent['encryptionHeader']
remoteIdentifier: FileContent['remoteIdentifier']
encryptedChunkSizes: FileContent['encryptedChunkSizes']
key: FileContent['key']
},
backupService: BackupServiceInterface,
crypto: PureCryptoInterface,
onDecryptedBytes: OnChunkCallback,
): Promise<'aborted' | 'failed' | 'success'> {
log(
LoggingDomain.FilesPackage,
'Reading and decrypting backup file',
file.uuid,
'chunk sizes',
file.encryptedChunkSizes,
)
const decryptor = new FileDecryptor(file, crypto)
const byteChunker = new OrderedByteChunker(file.encryptedChunkSizes, 'local', async (chunk) => {
log(LoggingDomain.FilesPackage, 'OrderedByteChunker did pop bytes', chunk.data.length, chunk.progress)
const decryptResult = decryptor.decryptBytes(chunk.data)
if (!decryptResult) {
return
}
await onDecryptedBytes({ ...chunk, data: decryptResult.decryptedBytes })
})
const readResult = await backupService.readEncryptedFileFromBackup(file.uuid, async (chunk) => {
log(LoggingDomain.FilesPackage, 'Got file chunk from backup service', chunk.data.length, chunk.progress)
await byteChunker.addBytes(chunk.data)
})
log(LoggingDomain.FilesPackage, 'Finished reading and decrypting backup file', file.uuid)
return readResult
}
@@ -3,4 +3,24 @@ export type FileDownloadProgress = {
encryptedBytesDownloaded: number
encryptedBytesRemaining: number
percentComplete: number
source: 'network' | 'local' | 'memcache'
}
export function fileProgressToHumanReadableString(
progress: FileDownloadProgress,
fileName: string,
options: { showPercent: boolean },
): string {
const progressPercent = Math.floor(progress.percentComplete)
const sourceString =
progress.source === 'network' ? '' : progress.source === 'memcache' ? 'from cache' : 'from backup'
let result = `Downloading file ${sourceString} "${fileName}"`
if (options.showPercent) {
result += ` (${progressPercent}%)`
}
return result
}
@@ -35,6 +35,7 @@ export class FileDownloader {
encryptedBytesDownloaded: this.totalBytesDownloaded,
encryptedBytesRemaining: encryptedSize - this.totalBytesDownloaded,
percentComplete: (this.totalBytesDownloaded / encryptedSize) * 100.0,
source: 'network',
}
}
+3 -1
View File
@@ -13,8 +13,10 @@ export * from './Device/FileBackupMetadataFile'
export * from './Device/FileBackupsConstantsV1'
export * from './Device/FileBackupsDevice'
export * from './Device/FileBackupsMapping'
export * from './Service/BackupServiceInterface'
export * from './Service/FilesClientInterface'
export * from './Service/ReadAndDecryptBackupFile'
export * from './Service/ReadAndDecryptBackupFileFileSystemAPI'
export * from './Service/ReadAndDecryptBackupFileUsingBackupService'
export * from './Operations/DownloadAndDecrypt'
export * from './Operations/EncryptAndUpload'
export * from './UseCase/FileDecryptor'
+6
View File
@@ -3,6 +3,12 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.6.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-28)
### Bug Fixes
* add new line after embedding files ([7f6c53d](https://github.com/standardnotes/app/commit/7f6c53dfcbc8952ca9eee76b9c4af70fea3c1bf1))
# [1.6.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-19)
### Features
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/icons",
"version": "1.6.0",
"version": "1.6.1",
"private": true,
"main": "dist/index.js",
"types": "dist/index.d.ts",
+1 -1
View File
@@ -1,4 +1,4 @@
<svg viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M9.99992 15C8.67384 15 7.40207 14.4732 6.46438 13.5355C5.5267 12.5978 4.99992 11.3261 4.99992 9.99998C4.99992 9.16665 5.20825 8.35831 5.58325 7.66665L4.36659 6.44998C3.71659 7.47498 3.33325 8.69165 3.33325 9.99998C3.33325 11.7681 4.03563 13.4638 5.28587 14.714C6.53612 15.9643 8.23181 16.6666 9.99992 16.6666V19.1666L13.3333 15.8333L9.99992 12.5V15ZM9.99992 3.33331V0.833313L6.66658 4.16665L9.99992 7.49998V4.99998C11.326 4.99998 12.5978 5.52676 13.5355 6.46445C14.4731 7.40213 14.9999 8.6739 14.9999 9.99998C14.9999 10.8333 14.7916 11.6416 14.4166 12.3333L15.6333 13.55C16.2833 12.525 16.6666 11.3083 16.6666 9.99998C16.6666 8.23187 15.9642 6.53618 14.714 5.28593C13.4637 4.03569 11.768 3.33331 9.99992 3.33331Z" />
<path d="M9.99999 15C11.3261 15 12.5978 14.4732 13.5355 13.5355C14.4732 12.5979 15 11.3261 15 10C15 9.16666 14.7917 8.35833 14.4167 7.66667L15.6333 6.45C16.2833 7.475 16.6667 8.69166 16.6667 10C16.6667 11.7681 15.9643 13.4638 14.714 14.714C13.4638 15.9643 11.7681 16.6667 9.99999 16.6667V19.1667L6.66666 15.8333L9.99999 12.5V15ZM9.99999 3.33333V0.833333L13.3333 4.16667L9.99999 7.5V5C8.67391 5 7.40214 5.52678 6.46446 6.46447C5.52677 7.40215 4.99999 8.67392 4.99999 10C4.99999 10.8333 5.20832 11.6417 5.58332 12.3333L4.36666 13.55C3.71666 12.525 3.33332 11.3083 3.33332 10C3.33332 8.23189 4.0357 6.5362 5.28594 5.28595C6.53619 4.03571 8.23188 3.33333 9.99999 3.33333Z" />
</svg>

Before

Width:  |  Height:  |  Size: 810 B

After

Width:  |  Height:  |  Size: 756 B

+128
View File
@@ -3,6 +3,134 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [3.49.9](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-05)
**Note:** Version bump only for package @standardnotes/mobile
## [3.49.8](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-05)
**Note:** Version bump only for package @standardnotes/mobile
## [3.49.7](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-05)
**Note:** Version bump only for package @standardnotes/mobile
## [3.49.6](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-05)
**Note:** Version bump only for package @standardnotes/mobile
## [3.49.5](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-04)
**Note:** Version bump only for package @standardnotes/mobile
## [3.49.4](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-04)
**Note:** Version bump only for package @standardnotes/mobile
## [3.49.3](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-02)
**Note:** Version bump only for package @standardnotes/mobile
## [3.49.2](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-02)
**Note:** Version bump only for package @standardnotes/mobile
## [3.49.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-02)
### Bug Fixes
* **mobile:** PDF preview causing loop when biometrics are set to Immediate ([#2080](https://github.com/standardnotes/app/issues/2080)) ([0c23e4d](https://github.com/standardnotes/app/commit/0c23e4d36cb46cfe66d47bb32a753eacd37eba63))
# [3.49.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-02)
### Features
* Moments: your personal photo journal, now available in Labs ([#2079](https://github.com/standardnotes/app/issues/2079)) ([29368c5](https://github.com/standardnotes/app/commit/29368c51b801331dd73d8cbee9746abadf2e1120))
## [3.48.7](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-01)
**Note:** Version bump only for package @standardnotes/mobile
## [3.48.6](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-01)
**Note:** Version bump only for package @standardnotes/mobile
## [3.48.5](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-30)
**Note:** Version bump only for package @standardnotes/mobile
## [3.48.4](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-30)
**Note:** Version bump only for package @standardnotes/mobile
## [3.48.3](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-30)
**Note:** Version bump only for package @standardnotes/mobile
## [3.48.2](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-30)
**Note:** Version bump only for package @standardnotes/mobile
## [3.48.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-29)
**Note:** Version bump only for package @standardnotes/mobile
# [3.48.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-28)
### Features
* display number of files for 'Files' view ([#2065](https://github.com/standardnotes/app/issues/2065)) ([767d354](https://github.com/standardnotes/app/commit/767d3547804bc98693dcc769baeed943c24426f3))
## [3.47.14](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-28)
**Note:** Version bump only for package @standardnotes/mobile
## [3.47.13](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-28)
**Note:** Version bump only for package @standardnotes/mobile
## [3.47.12](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-28)
**Note:** Version bump only for package @standardnotes/mobile
## [3.47.11](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-28)
### Bug Fixes
* Super editor popover menus on iOS ([#2061](https://github.com/standardnotes/app/issues/2061)) ([148aee4](https://github.com/standardnotes/app/commit/148aee441b816c58599d95db2e5b0f3f93414511))
## [3.47.10](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-28)
**Note:** Version bump only for package @standardnotes/mobile
## [3.47.9](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-28)
**Note:** Version bump only for package @standardnotes/mobile
## [3.47.8](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-27)
**Note:** Version bump only for package @standardnotes/mobile
## [3.47.7](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-27)
**Note:** Version bump only for package @standardnotes/mobile
## [3.47.6](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-27)
**Note:** Version bump only for package @standardnotes/mobile
## [3.47.5](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-27)
**Note:** Version bump only for package @standardnotes/mobile
## [3.47.4](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-25)
**Note:** Version bump only for package @standardnotes/mobile
## [3.47.3](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-25)
**Note:** Version bump only for package @standardnotes/mobile
## [3.47.2](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-24)
**Note:** Version bump only for package @standardnotes/mobile
+2 -2
View File
@@ -583,7 +583,7 @@ EXTERNAL SOURCES:
SPEC CHECKSUMS:
boost: a7c83b31436843459a1961bfd74b96033dc77234
CocoaAsyncSocket: 065fd1e645c7abab64f7a6a2007a48038fdc6a99
DoubleConversion: 5189b271737e1565bdce30deb4a08d647e3f5f54
DoubleConversion: 831926d9b8bf8166fd87886c4abab286c2422662
FBLazyVector: 48289402952f4f7a4e235de70a9a590aa0b79ef4
FBReactNativeSpec: dd1186fd05255e3457baa2f4ca65e94c2cd1e3ac
Flipper: 26fc4b7382499f1281eb8cb921e5c3ad6de91fe0
@@ -596,7 +596,7 @@ SPEC CHECKSUMS:
Flipper-RSocket: d9d9ade67cbecf6ac10730304bf5607266dd2541
FlipperKit: cbdee19bdd4e7f05472a66ce290f1b729ba3cb86
fmt: ff9d55029c625d3757ed641535fd4a75fedc7ce9
glog: 04b94705f318337d7ead9e6d17c019bd9b1f6b1b
glog: 85ecdd10ee8d8ec362ef519a6a45ff9aa27b2e85
hermes-engine: 2af7b7a59128f250adfd86f15aa1d5a2ecd39995
libevent: 4049cae6c81cdb3654a443be001fb9bdceff7913
OpenSSL-Universal: ebc357f1e6bc71fa463ccb2fe676756aff50e88c
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/mobile",
"version": "3.47.2",
"version": "3.49.9",
"author": "Standard Notes.",
"private": true,
"license": "AGPL-3.0-or-later",
@@ -253,6 +253,9 @@ const MobileWebAppContents = ({ destroyAndReload }: { destroyAndReload: () => vo
return true
}
const requireInlineMediaPlaybackForMomentsFeature = true
const requireMediaUserInteractionForMomentsFeature = false
return (
<WebView
ref={webViewRef}
@@ -274,6 +277,8 @@ const MobileWebAppContents = ({ destroyAndReload }: { destroyAndReload: () => vo
injectedJavaScriptBeforeContentLoaded={injectedJS}
bounces={false}
keyboardDisplayRequiresUserAction={false}
allowsInlineMediaPlayback={requireInlineMediaPlaybackForMomentsFeature}
mediaPlaybackRequiresUserAction={requireMediaUserInteractionForMomentsFeature}
scalesPageToFit={true}
/**
* This disables the global window scroll but keeps scroll within div elements like lists and textareas.
+38
View File
@@ -3,6 +3,44 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.39.2](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-05)
**Note:** Version bump only for package @standardnotes/models
## [1.39.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-02)
**Note:** Version bump only for package @standardnotes/models
# [1.39.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-02)
### Features
* Moments: your personal photo journal, now available in Labs ([#2079](https://github.com/standardnotes/app/issues/2079)) ([29368c5](https://github.com/standardnotes/app/commit/29368c51b801331dd73d8cbee9746abadf2e1120))
## [1.38.2](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-01)
**Note:** Version bump only for package @standardnotes/models
## [1.38.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-01)
**Note:** Version bump only for package @standardnotes/models
# [1.38.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-28)
### Features
* display number of files for 'Files' view ([#2065](https://github.com/standardnotes/app/issues/2065)) ([767d354](https://github.com/standardnotes/app/commit/767d3547804bc98693dcc769baeed943c24426f3))
## [1.37.9](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-28)
### Bug Fixes
* exclude files from being counted in 'Notes' view ([#2062](https://github.com/standardnotes/app/issues/2062)) ([2b0c9b1](https://github.com/standardnotes/app/commit/2b0c9b188cd75477c0c1611443daadb8ffa29306))
## [1.37.8](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-27)
**Note:** Version bump only for package @standardnotes/models
## [1.37.7](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-23)
**Note:** Version bump only for package @standardnotes/models
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/models",
"version": "1.37.7",
"version": "1.39.2",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
@@ -12,6 +12,7 @@ export type TagItemCountChangeObserver = (tagUuid: Uuid | AllNotesUuidSignifier)
export class TagItemsIndex implements SNIndex {
private tagToItemsMap: Partial<Record<Uuid, Set<Uuid>>> = {}
private allCountableItems = new Set<Uuid>()
private countableItemsByType = new Map<ContentType, Set<Uuid>>()
constructor(private collection: ItemCollection, public observers: TagItemCountChangeObserver[] = []) {}
@@ -41,6 +42,14 @@ export class TagItemsIndex implements SNIndex {
return this.allCountableItems.size
}
public allCountableNotesCount(): number {
return this.countableItemsByType.get(ContentType.Note)?.size || 0
}
public allCountableFilesCount(): number {
return this.countableItemsByType.get(ContentType.File)?.size || 0
}
public countableItemsForTag(tag: SNTag): number {
return this.tagToItemsMap[tag.uuid]?.size || 0
}
@@ -75,10 +84,18 @@ export class TagItemsIndex implements SNIndex {
for (const item of items) {
const isCountable = this.isItemCountable(item)
if (isCountable) {
this.allCountableItems.add(item.uuid)
if (!this.countableItemsByType.has(item.content_type)) {
this.countableItemsByType.set(item.content_type, new Set())
}
this.countableItemsByType.get(item.content_type)?.add(item.uuid)
} else {
this.allCountableItems.delete(item.uuid)
this.countableItemsByType.get(item.content_type)?.delete(item.uuid)
}
const associatedTagUuids = this.collection.uuidsThatReferenceUuid(item.uuid)
@@ -1,15 +1,16 @@
import { dateFromDSLDateString } from './Utils'
import { addDaysToDate, addHoursToDate } from '@standardnotes/utils'
describe('Predicate Utils', () => {
describe('dateFromDSLDateString', () => {
it('should return a date object with the correct day', () => {
const date = dateFromDSLDateString('1.days.ago')
expect(date.getDate()).toEqual(new Date().getDate() - 1)
expect(date.getDate()).toEqual(addDaysToDate(new Date(), -1).getDate())
})
it('should return a date object with the correct hour', () => {
const date = dateFromDSLDateString('1.hours.ago')
expect(date.getHours()).toEqual(new Date().getHours() - 1)
expect(date.getHours()).toEqual(addHoursToDate(new Date(), -1).getHours())
})
it('should return a date object with the correct month', () => {
@@ -12,10 +12,7 @@ import { PayloadTimestampDefaults } from '../../Abstract/Payload'
import { FilterDisplayOptions } from '../../Runtime/Display'
import { FileItem } from '../File'
export function BuildSmartViews(
options: FilterDisplayOptions,
{ supportsFileNavigation = false }: { supportsFileNavigation: boolean },
): SmartView[] {
export function BuildSmartViews(options: FilterDisplayOptions): SmartView[] {
const notes = new SmartView(
new DecryptedPayload({
uuid: SystemViewId.AllNotes,
@@ -88,11 +85,7 @@ export function BuildSmartViews(
}),
)
if (supportsFileNavigation) {
return [notes, starred, files, archived, trash, untagged]
} else {
return [notes, starred, archived, trash, untagged]
}
return [notes, files, starred, archived, trash, untagged]
}
function allNotesPredicate(options: FilterDisplayOptions) {
@@ -39,6 +39,7 @@ export enum PrefKey {
UpdateSavingStatusIndicator = 'updateSavingStatusIndicator',
DarkMode = 'darkMode',
DefaultEditorIdentifier = 'defaultEditorIdentifier',
MomentsDefaultTagUuid = 'momentsDefaultTagUuid',
}
export enum NewNoteTitleFormat {
@@ -103,4 +104,5 @@ export type PrefValue = {
[PrefKey.UpdateSavingStatusIndicator]: boolean
[PrefKey.DarkMode]: boolean
[PrefKey.DefaultEditorIdentifier]: EditorIdentifier
[PrefKey.MomentsDefaultTagUuid]: string | undefined
}
@@ -1,6 +1,6 @@
export type VectorIconNameOrEmoji = EmojiString | IconType
export type EmojiString = string
export type EmojiString = Omit<string, IconType>
export type IconType =
| 'accessibility'
+120
View File
@@ -3,6 +3,126 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.4.33](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-05)
**Note:** Version bump only for package @standardnotes/releases
## [1.4.32](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-05)
**Note:** Version bump only for package @standardnotes/releases
## [1.4.31](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-05)
**Note:** Version bump only for package @standardnotes/releases
## [1.4.30](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-05)
**Note:** Version bump only for package @standardnotes/releases
## [1.4.29](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-04)
**Note:** Version bump only for package @standardnotes/releases
## [1.4.28](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-04)
**Note:** Version bump only for package @standardnotes/releases
## [1.4.27](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-02)
**Note:** Version bump only for package @standardnotes/releases
## [1.4.26](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-02)
**Note:** Version bump only for package @standardnotes/releases
## [1.4.25](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-02)
**Note:** Version bump only for package @standardnotes/releases
## [1.4.24](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-02)
**Note:** Version bump only for package @standardnotes/releases
## [1.4.23](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-01)
**Note:** Version bump only for package @standardnotes/releases
## [1.4.22](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-01)
**Note:** Version bump only for package @standardnotes/releases
## [1.4.21](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-30)
**Note:** Version bump only for package @standardnotes/releases
## [1.4.20](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-30)
**Note:** Version bump only for package @standardnotes/releases
## [1.4.19](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-30)
**Note:** Version bump only for package @standardnotes/releases
## [1.4.18](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-30)
**Note:** Version bump only for package @standardnotes/releases
## [1.4.17](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-29)
**Note:** Version bump only for package @standardnotes/releases
## [1.4.16](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-28)
**Note:** Version bump only for package @standardnotes/releases
## [1.4.15](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-28)
**Note:** Version bump only for package @standardnotes/releases
## [1.4.14](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-28)
**Note:** Version bump only for package @standardnotes/releases
## [1.4.13](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-28)
**Note:** Version bump only for package @standardnotes/releases
## [1.4.12](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-28)
**Note:** Version bump only for package @standardnotes/releases
## [1.4.11](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-28)
**Note:** Version bump only for package @standardnotes/releases
## [1.4.10](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-28)
**Note:** Version bump only for package @standardnotes/releases
## [1.4.9](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-27)
**Note:** Version bump only for package @standardnotes/releases
## [1.4.8](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-27)
**Note:** Version bump only for package @standardnotes/releases
## [1.4.7](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-27)
**Note:** Version bump only for package @standardnotes/releases
## [1.4.6](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-27)
**Note:** Version bump only for package @standardnotes/releases
## [1.4.5](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-25)
**Note:** Version bump only for package @standardnotes/releases
## [1.4.4](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-25)
**Note:** Version bump only for package @standardnotes/releases
## [1.4.3](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-24)
**Note:** Version bump only for package @standardnotes/releases
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/releases",
"version": "1.4.3",
"version": "1.4.33",
"license": "AGPL-3.0-or-later",
"main": "dist/releases.json",
"types": "dist/index.d.ts",
+4
View File
@@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.12.10](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-05)
**Note:** Version bump only for package @standardnotes/responses
## [1.12.9](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-17)
**Note:** Version bump only for package @standardnotes/responses
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/responses",
"version": "1.12.9",
"version": "1.12.10",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
+52
View File
@@ -3,6 +3,58 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.50.4](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-05)
**Note:** Version bump only for package @standardnotes/services
## [1.50.3](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-04)
### Bug Fixes
* **mobile:** Fixed issue where pressing the back button on editor screen wouldn't switch back to the items list on Android ([07cb00a](https://github.com/standardnotes/app/commit/07cb00a81505ed880a2b8033b3e69c53dd9b48b2))
## [1.50.2](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-02)
### Bug Fixes
* fixes issue where Moments camera wouldn't properly start on desktop app ([#2084](https://github.com/standardnotes/app/issues/2084)) ([8c41f0e](https://github.com/standardnotes/app/commit/8c41f0ecc30662c5e2fef72e5685056a8cddce62))
## [1.50.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-02)
### Bug Fixes
* fixes issue where files imported from another account could not be deleted ([#2082](https://github.com/standardnotes/app/issues/2082)) ([b5b6192](https://github.com/standardnotes/app/commit/b5b6192b603b5ce60b913cc7138d30e46aafbe14))
# [1.50.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-02)
### Features
* Moments: your personal photo journal, now available in Labs ([#2079](https://github.com/standardnotes/app/issues/2079)) ([29368c5](https://github.com/standardnotes/app/commit/29368c51b801331dd73d8cbee9746abadf2e1120))
# [1.49.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-01)
### Features
* download and preview files from local backups automatically, if a local backup is available ([#2076](https://github.com/standardnotes/app/issues/2076)) ([28e43d3](https://github.com/standardnotes/app/commit/28e43d37c00c5cd97129c4b83266f7a234bd9857))
## [1.48.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-01)
**Note:** Version bump only for package @standardnotes/services
# [1.48.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-28)
### Features
* display number of files for 'Files' view ([#2065](https://github.com/standardnotes/app/issues/2065)) ([767d354](https://github.com/standardnotes/app/commit/767d3547804bc98693dcc769baeed943c24426f3))
## [1.47.2](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-28)
**Note:** Version bump only for package @standardnotes/services
## [1.47.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-27)
**Note:** Version bump only for package @standardnotes/services
# [1.47.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-24)
### Features
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/services",
"version": "1.47.0",
"version": "1.50.4",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
@@ -13,7 +13,7 @@
"tsc": "tsc --project tsconfig.json",
"lint": "eslint src --ext .ts",
"lint:fix": "eslint src --ext .ts --fix",
"test": "jest spec --coverage"
"test": "jest --coverage"
},
"dependencies": {
"@standardnotes/api": "workspace:^",
@@ -17,4 +17,5 @@ export interface WebApplicationInterface extends ApplicationInterface {
mobileDevice(): MobileDeviceInterface
handleAndroidBackButtonPressed(): void
addAndroidBackHandlerEventListener(listener: () => boolean): (() => void) | undefined
setAndroidBackHandlerFallbackListener(listener: () => boolean): void
}
@@ -0,0 +1,109 @@
import { StatusServiceInterface } from './../Status/StatusServiceInterface'
import { FilesBackupService } from './BackupService'
import { PureCryptoInterface, StreamEncryptor } from '@standardnotes/sncrypto-common'
import { EncryptionProviderInterface } from '@standardnotes/encryption'
import { ItemManagerInterface } from '../Item/ItemManagerInterface'
import { InternalEventBusInterface } from '..'
import { AlertService } from '../Alert/AlertService'
import { ApiServiceInterface } from '../Api/ApiServiceInterface'
import { SyncServiceInterface } from '../Sync/SyncServiceInterface'
import { FileBackupsDevice } from '@standardnotes/files'
describe('backup service', () => {
let apiService: ApiServiceInterface
let itemManager: ItemManagerInterface
let syncService: SyncServiceInterface
let alertService: AlertService
let crypto: PureCryptoInterface
let status: StatusServiceInterface
let encryptor: EncryptionProviderInterface
let internalEventBus: InternalEventBusInterface
let backupService: FilesBackupService
let device: FileBackupsDevice
beforeEach(() => {
apiService = {} as jest.Mocked<ApiServiceInterface>
apiService.addEventObserver = jest.fn()
apiService.createFileValetToken = jest.fn()
apiService.downloadFile = jest.fn()
apiService.deleteFile = jest.fn().mockReturnValue({})
itemManager = {} as jest.Mocked<ItemManagerInterface>
itemManager.createItem = jest.fn()
itemManager.createTemplateItem = jest.fn().mockReturnValue({})
itemManager.setItemToBeDeleted = jest.fn()
itemManager.addObserver = jest.fn()
itemManager.changeItem = jest.fn()
status = {} as jest.Mocked<StatusServiceInterface>
device = {} as jest.Mocked<FileBackupsDevice>
device.getFileBackupReadToken = jest.fn()
device.readNextChunk = jest.fn()
syncService = {} as jest.Mocked<SyncServiceInterface>
syncService.sync = jest.fn()
encryptor = {} as jest.Mocked<EncryptionProviderInterface>
alertService = {} as jest.Mocked<AlertService>
alertService.confirm = jest.fn().mockReturnValue(true)
alertService.alert = jest.fn()
crypto = {} as jest.Mocked<PureCryptoInterface>
crypto.base64Decode = jest.fn()
internalEventBus = {} as jest.Mocked<InternalEventBusInterface>
internalEventBus.publish = jest.fn()
backupService = new FilesBackupService(itemManager, apiService, encryptor, device, status, crypto, internalEventBus)
crypto.xchacha20StreamInitDecryptor = jest.fn().mockReturnValue({
state: {},
} as StreamEncryptor)
crypto.xchacha20StreamDecryptorPush = jest.fn().mockReturnValue({ message: new Uint8Array([0xaa]), tag: 0 })
crypto.xchacha20StreamInitEncryptor = jest.fn().mockReturnValue({
header: 'some-header',
state: {},
} as StreamEncryptor)
crypto.xchacha20StreamEncryptorPush = jest.fn().mockReturnValue(new Uint8Array())
})
describe('readEncryptedFileFromBackup', () => {
it('return failed if no backup', async () => {
backupService.getFileBackupInfo = jest.fn().mockReturnValue(undefined)
const result = await backupService.readEncryptedFileFromBackup('123', async () => {})
expect(result).toEqual('failed')
})
it('return success if backup', async () => {
backupService.getFileBackupInfo = jest.fn().mockReturnValue({})
device.readNextChunk = jest.fn().mockReturnValue({ chunk: new Uint8Array([]), isLast: true, progress: undefined })
const result = await backupService.readEncryptedFileFromBackup('123', async () => {})
expect(result).toEqual('success')
})
it('should loop through all chunks until last', async () => {
backupService.getFileBackupInfo = jest.fn().mockReturnValue({})
const expectedChunkCount = 3
let receivedChunkCount = 0
const mockFn = (device.readNextChunk = jest.fn().mockImplementation(() => {
receivedChunkCount++
return { chunk: new Uint8Array([]), isLast: receivedChunkCount === expectedChunkCount, progress: undefined }
}))
await backupService.readEncryptedFileFromBackup('123', async () => {})
expect(mockFn.mock.calls.length).toEqual(expectedChunkCount)
})
})
})
@@ -8,13 +8,17 @@ import {
FileBackupsDevice,
FileBackupsMapping,
FileBackupRecord,
OnChunkCallback,
BackupServiceInterface,
} from '@standardnotes/files'
import { InternalEventBusInterface } from '../Internal/InternalEventBusInterface'
import { ItemManagerInterface } from '../Item/ItemManagerInterface'
import { AbstractService } from '../Service/AbstractService'
import { StatusServiceInterface } from '../Status/StatusServiceInterface'
import { PureCryptoInterface } from '@standardnotes/sncrypto-common'
import { log, LoggingDomain } from '../Logging'
export class FilesBackupService extends AbstractService {
export class FilesBackupService extends AbstractService implements BackupServiceInterface {
private itemsObserverDisposer: () => void
private pendingFiles = new Set<Uuid>()
private mappingCache?: FileBackupsMapping['files']
@@ -25,6 +29,7 @@ export class FilesBackupService extends AbstractService {
private encryptor: EncryptionProviderInterface,
private device: FileBackupsDevice,
private status: StatusServiceInterface,
private crypto: PureCryptoInterface,
protected override internalEventBus: InternalEventBusInterface,
) {
super(internalEventBus)
@@ -43,7 +48,14 @@ export class FilesBackupService extends AbstractService {
}
override deinit() {
super.deinit()
this.itemsObserverDisposer()
;(this.items as unknown) = undefined
;(this.api as unknown) = undefined
;(this.encryptor as unknown) = undefined
;(this.device as unknown) = undefined
;(this.status as unknown) = undefined
;(this.crypto as unknown) = undefined
}
public isFilesBackupsEnabled(): Promise<boolean> {
@@ -98,7 +110,7 @@ export class FilesBackupService extends AbstractService {
return this.mappingCache ?? (await this.getBackupsMappingFromDisk())
}
public async getFileBackupInfo(file: FileItem): Promise<FileBackupRecord | undefined> {
public async getFileBackupInfo(file: { uuid: string }): Promise<FileBackupRecord | undefined> {
const mapping = await this.getBackupsMappingFromCache()
const record = mapping[file.uuid]
return record
@@ -138,7 +150,34 @@ export class FilesBackupService extends AbstractService {
this.invalidateMappingCache()
}
async readEncryptedFileFromBackup(uuid: string, onChunk: OnChunkCallback): Promise<'success' | 'failed' | 'aborted'> {
const fileBackup = await this.getFileBackupInfo({ uuid })
if (!fileBackup) {
return 'failed'
}
const token = await this.device.getFileBackupReadToken(fileBackup)
let readMore = true
let index = 0
while (readMore) {
const { chunk, isLast, progress } = await this.device.readNextChunk(token)
await onChunk({ data: chunk, index, isLast, progress })
readMore = !isLast
index++
}
return 'success'
}
private async performBackupOperation(file: FileItem): Promise<'success' | 'failed' | 'aborted'> {
log(LoggingDomain.FilesBackups, 'Backing up file locally', file.uuid)
const messageId = this.status.addMessage(`Backing up file ${file.name}...`)
const encryptedFile = await this.encryptor.encryptSplitSingle({
@@ -19,6 +19,8 @@ export interface WebClientRequiresDesktopMethods extends FileBackupsDevice {
downloadBackup(): void | Promise<void>
get extensionsServerHost(): string
askForMediaAccess(type: 'camera' | 'microphone'): Promise<boolean>
}
export interface DesktopClientRequiresWebMethods {
@@ -1,15 +1,14 @@
import { PureCryptoInterface, StreamEncryptor } from '@standardnotes/sncrypto-common'
import { FileItem } from '@standardnotes/models'
import { EncryptionProviderInterface } from '@standardnotes/encryption'
import { ItemManagerInterface } from '../Item/ItemManagerInterface'
import { ChallengeServiceInterface } from '../Challenge'
import { InternalEventBusInterface } from '..'
import { AlertService } from '../Alert/AlertService'
import { ApiServiceInterface } from '../Api/ApiServiceInterface'
import { SyncServiceInterface } from '../Sync/SyncServiceInterface'
import { FileService } from './FileService'
import { BackupServiceInterface } from '@standardnotes/files'
describe('fileService', () => {
let apiService: ApiServiceInterface
@@ -21,13 +20,33 @@ describe('fileService', () => {
let fileService: FileService
let encryptor: EncryptionProviderInterface
let internalEventBus: InternalEventBusInterface
let backupService: BackupServiceInterface
beforeEach(() => {
apiService = {} as jest.Mocked<ApiServiceInterface>
apiService.addEventObserver = jest.fn()
apiService.createFileValetToken = jest.fn()
apiService.downloadFile = jest.fn()
apiService.deleteFile = jest.fn().mockReturnValue({})
const numChunks = 1
apiService.downloadFile = jest
.fn()
.mockImplementation(
(
_file: string,
_chunkIndex: number,
_apiToken: string,
_rangeStart: number,
onBytesReceived: (bytes: Uint8Array) => void,
) => {
return new Promise<void>((resolve) => {
for (let i = 0; i < numChunks; i++) {
onBytesReceived(Uint8Array.from([0xaa]))
}
resolve()
})
},
)
itemManager = {} as jest.Mocked<ItemManagerInterface>
itemManager.createItem = jest.fn()
@@ -52,6 +71,10 @@ describe('fileService', () => {
internalEventBus = {} as jest.Mocked<InternalEventBusInterface>
internalEventBus.publish = jest.fn()
backupService = {} as jest.Mocked<BackupServiceInterface>
backupService.readEncryptedFileFromBackup = jest.fn()
backupService.getFileBackupInfo = jest.fn()
fileService = new FileService(
apiService,
itemManager,
@@ -61,6 +84,7 @@ describe('fileService', () => {
alertService,
crypto,
internalEventBus,
backupService,
)
crypto.xchacha20StreamInitDecryptor = jest.fn().mockReturnValue({
@@ -110,6 +134,8 @@ describe('fileService', () => {
decryptedSize: 100_000,
} as jest.Mocked<FileItem>
apiService.downloadFile = jest.fn()
await fileService.downloadFile(file, async () => {
return Promise.resolve()
})
@@ -118,4 +144,59 @@ describe('fileService', () => {
expect(fileService['encryptedCache'].get(file.uuid)).toBeFalsy()
})
it('if file fails to delete, should present alert asking if they want to remove item', async () => {
const file = {
uuid: '1',
decryptedSize: 100_000,
} as jest.Mocked<FileItem>
const alertMock = (alertService.confirm = jest.fn().mockReturnValue(true))
const deleteItemMock = (itemManager.setItemToBeDeleted = jest.fn())
apiService.deleteFile = jest.fn().mockReturnValue({ error: true })
await fileService.deleteFile(file)
expect(alertMock).toHaveBeenCalledTimes(1)
expect(deleteItemMock).toHaveBeenCalledTimes(1)
})
it('should download file from network if no backup', async () => {
const file = {
uuid: '1',
decryptedSize: 100_000,
encryptedSize: 101_000,
encryptedChunkSizes: [101_000],
} as jest.Mocked<FileItem>
backupService.getFileBackupInfo = jest.fn().mockReturnValue(undefined)
const downloadMock = apiService.downloadFile as jest.Mock
await fileService.downloadFile(file, async () => {
return Promise.resolve()
})
expect(downloadMock).toHaveBeenCalledTimes(1)
})
it('should download file from local backup if it exists', async () => {
const file = {
uuid: '1',
decryptedSize: 100_000,
encryptedSize: 101_000,
encryptedChunkSizes: [101_000],
} as jest.Mocked<FileItem>
backupService.getFileBackupInfo = jest.fn().mockReturnValue({})
const downloadMock = (apiService.downloadFile = jest.fn())
await fileService.downloadFile(file, async () => {
return Promise.resolve()
})
expect(downloadMock).toHaveBeenCalledTimes(0)
})
})
@@ -11,7 +11,7 @@ import {
isEncryptedPayload,
} from '@standardnotes/models'
import { PureCryptoInterface } from '@standardnotes/sncrypto-common'
import { UuidGenerator } from '@standardnotes/utils'
import { spaceSeparatedStrings, UuidGenerator } from '@standardnotes/utils'
import { EncryptionProviderInterface, SNItemsKey } from '@standardnotes/encryption'
import {
DownloadAndDecryptFileOperation,
@@ -19,7 +19,7 @@ import {
FileDecryptor,
FileDownloadProgress,
FilesClientInterface,
readAndDecryptBackupFile,
readAndDecryptBackupFileUsingFileSystemAPI,
FilesApiInterface,
FileBackupsConstantsV1,
FileBackupMetadataFile,
@@ -30,15 +30,17 @@ import {
DecryptedBytes,
OrderedByteChunker,
FileMemoryCache,
readAndDecryptBackupFileUsingBackupService,
BackupServiceInterface,
} from '@standardnotes/files'
import { AlertService } from '../Alert/AlertService'
import { AlertService, ButtonType } from '../Alert/AlertService'
import { ChallengeServiceInterface } from '../Challenge'
import { InternalEventBusInterface } from '../Internal/InternalEventBusInterface'
import { ItemManagerInterface } from '../Item/ItemManagerInterface'
import { AbstractService } from '../Service/AbstractService'
import { SyncServiceInterface } from '../Sync/SyncServiceInterface'
import { DecryptItemsKeyWithUserFallback } from '../Encryption/Functions'
import { log, LoggingDomain } from '../Logging'
const OneHundredMb = 100 * 1_000_000
@@ -54,6 +56,7 @@ export class FileService extends AbstractService implements FilesClientInterface
private alertService: AlertService,
private crypto: PureCryptoInterface,
protected override internalEventBus: InternalEventBusInterface,
private backupsService?: BackupServiceInterface,
) {
super(internalEventBus)
}
@@ -158,8 +161,8 @@ export class FileService extends AbstractService implements FilesClientInterface
let decryptedAggregate = new Uint8Array()
const orderedChunker = new OrderedByteChunker(file.encryptedChunkSizes, async (encryptedBytes) => {
const decryptedBytes = decryptOperation.decryptBytes(encryptedBytes)
const orderedChunker = new OrderedByteChunker(file.encryptedChunkSizes, 'memcache', async (chunk) => {
const decryptedBytes = decryptOperation.decryptBytes(chunk.data)
if (decryptedBytes) {
decryptedAggregate = new Uint8Array([...decryptedAggregate, ...decryptedBytes.decryptedBytes])
@@ -173,36 +176,60 @@ export class FileService extends AbstractService implements FilesClientInterface
public async downloadFile(
file: FileItem,
onDecryptedBytes: (decryptedBytes: Uint8Array, progress?: FileDownloadProgress) => Promise<void>,
onDecryptedBytes: (decryptedBytes: Uint8Array, progress: FileDownloadProgress) => Promise<void>,
): Promise<ClientDisplayableError | undefined> {
const cachedBytes = this.encryptedCache.get(file.uuid)
if (cachedBytes) {
const decryptedBytes = await this.decryptCachedEntry(file, cachedBytes)
await onDecryptedBytes(decryptedBytes.decryptedBytes, undefined)
await onDecryptedBytes(decryptedBytes.decryptedBytes, {
encryptedFileSize: cachedBytes.encryptedBytes.length,
encryptedBytesDownloaded: cachedBytes.encryptedBytes.length,
encryptedBytesRemaining: 0,
percentComplete: 100,
source: 'memcache',
})
return undefined
}
const addToCache = file.encryptedSize < this.encryptedCache.maxSize
const fileBackup = await this.backupsService?.getFileBackupInfo(file)
let cacheEntryAggregate = new Uint8Array()
if (this.backupsService && fileBackup) {
log(LoggingDomain.FilesService, 'Downloading file from backup', fileBackup)
const operation = new DownloadAndDecryptFileOperation(file, this.crypto, this.api)
await readAndDecryptBackupFileUsingBackupService(file, this.backupsService, this.crypto, async (chunk) => {
log(LoggingDomain.FilesService, 'Got local file chunk', chunk.progress)
const result = await operation.run(async ({ decrypted, encrypted, progress }): Promise<void> => {
if (addToCache) {
cacheEntryAggregate = new Uint8Array([...cacheEntryAggregate, ...encrypted.encryptedBytes])
return onDecryptedBytes(chunk.data, chunk.progress)
})
log(LoggingDomain.FilesService, 'Finished downloading file from backup')
return undefined
} else {
log(LoggingDomain.FilesService, 'Downloading file from network')
const addToCache = file.encryptedSize < this.encryptedCache.maxSize
let cacheEntryAggregate = new Uint8Array()
const operation = new DownloadAndDecryptFileOperation(file, this.crypto, this.api)
const result = await operation.run(async ({ decrypted, encrypted, progress }): Promise<void> => {
if (addToCache) {
cacheEntryAggregate = new Uint8Array([...cacheEntryAggregate, ...encrypted.encryptedBytes])
}
return onDecryptedBytes(decrypted.decryptedBytes, progress)
})
if (addToCache && cacheEntryAggregate.byteLength > 0) {
this.encryptedCache.add(file.uuid, { encryptedBytes: cacheEntryAggregate })
}
return onDecryptedBytes(decrypted.decryptedBytes, progress)
})
if (addToCache) {
this.encryptedCache.add(file.uuid, { encryptedBytes: cacheEntryAggregate })
return result.error
}
return result.error
}
public async deleteFile(file: FileItem): Promise<ClientDisplayableError | undefined> {
@@ -217,7 +244,21 @@ export class FileService extends AbstractService implements FilesClientInterface
const result = await this.api.deleteFile(tokenResult)
if (result.error) {
return ClientDisplayableError.FromError(result.error)
const deleteAnyway = await this.alertService.confirm(
spaceSeparatedStrings(
'This file could not be deleted from the server, possibly because you are attempting to delete a file item',
'that was imported from another account. Would you like to remove this file item from your account anyway?',
"If you're sure the file is yours and still exists on the server, do not choose this option,",
'and instead try to delete it again.',
),
'Unable to Delete',
'Delete Anyway',
ButtonType.Danger,
)
if (!deleteAnyway) {
return ClientDisplayableError.FromError(result.error)
}
}
await this.itemManager.setItemToBeDeleted(file)
@@ -294,9 +335,15 @@ export class FileService extends AbstractService implements FilesClientInterface
return destinationFileHandle
}
const result = await readAndDecryptBackupFile(fileHandle, file, fileSystem, this.crypto, async (decryptedBytes) => {
await fileSystem.saveBytes(destinationFileHandle, decryptedBytes)
})
const result = await readAndDecryptBackupFileUsingFileSystemAPI(
fileHandle,
file,
fileSystem,
this.crypto,
async (decryptedBytes) => {
await fileSystem.saveBytes(destinationFileHandle, decryptedBytes)
},
)
await fileSystem.closeFileWriteStream(destinationFileHandle)
@@ -310,9 +357,15 @@ export class FileService extends AbstractService implements FilesClientInterface
): Promise<Uint8Array> {
let bytes = new Uint8Array()
await readAndDecryptBackupFile(fileHandle, file, fileSystem, this.crypto, async (decryptedBytes) => {
bytes = new Uint8Array([...bytes, ...decryptedBytes])
})
await readAndDecryptBackupFileUsingFileSystemAPI(
fileHandle,
file,
fileSystem,
this.crypto,
async (decryptedBytes) => {
bytes = new Uint8Array([...bytes, ...decryptedBytes])
},
)
return bytes
}
@@ -60,6 +60,7 @@ export interface ItemsClientInterface {
addNoteCountChangeObserver(observer: TagItemCountChangeObserver): () => void
allCountableNotesCount(): number
allCountableFilesCount(): number
countableNotesForTag(tag: SNTag | SmartView): number
+33
View File
@@ -0,0 +1,33 @@
import { logWithColor } from '@standardnotes/utils'
declare const process: {
env: {
NODE_ENV: string | null | undefined
}
}
export const isDev = process.env.NODE_ENV === 'development' || process.env.NODE_ENV === 'test'
export enum LoggingDomain {
FilesService,
FilesBackups,
}
const LoggingStatus: Record<LoggingDomain, boolean> = {
[LoggingDomain.FilesService]: false,
[LoggingDomain.FilesBackups]: false,
}
const LoggingColor: Record<LoggingDomain, string> = {
[LoggingDomain.FilesService]: 'blue',
[LoggingDomain.FilesBackups]: 'yellow',
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export function log(domain: LoggingDomain, ...args: any[]): void {
if (!isDev || !LoggingStatus[domain]) {
return
}
logWithColor(LoggingDomain[domain], LoggingColor[domain], ...args)
}
@@ -40,6 +40,7 @@ export enum StorageKey {
CodeVerifier = 'code_verifier',
LaunchPriorityUuids = 'launch_priority_uuids',
LastReadChangelogVersion = 'last_read_changelog_version',
MomentsEnabled = 'moments_enabled',
}
export enum NonwrappedStorageKey {
+54
View File
@@ -3,6 +3,60 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [2.156.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-05)
### Features
* add Super note type to list of note types ([#2086](https://github.com/standardnotes/app/issues/2086)) ([caf2c4a](https://github.com/standardnotes/app/commit/caf2c4a87646ab4d1aa7007c39547fd3b16c0c6f))
## [2.155.4](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-04)
**Note:** Version bump only for package @standardnotes/snjs
## [2.155.3](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-02)
**Note:** Version bump only for package @standardnotes/snjs
## [2.155.2](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-02)
**Note:** Version bump only for package @standardnotes/snjs
## [2.155.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-02)
**Note:** Version bump only for package @standardnotes/snjs
# [2.155.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-01)
### Features
* download and preview files from local backups automatically, if a local backup is available ([#2076](https://github.com/standardnotes/app/issues/2076)) ([28e43d3](https://github.com/standardnotes/app/commit/28e43d37c00c5cd97129c4b83266f7a234bd9857))
## [2.154.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-01)
**Note:** Version bump only for package @standardnotes/snjs
# [2.154.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-28)
### Features
* display number of files for 'Files' view ([#2065](https://github.com/standardnotes/app/issues/2065)) ([767d354](https://github.com/standardnotes/app/commit/767d3547804bc98693dcc769baeed943c24426f3))
# [2.153.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-28)
### Features
* convert Super notes to Markdown behind the scenes while publishing to Listed ([#2064](https://github.com/standardnotes/app/issues/2064)) ([cc2762a](https://github.com/standardnotes/app/commit/cc2762a29d83e2e4cd646345f9d3935571b27c62))
## [2.152.6](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-28)
### Bug Fixes
* exclude files from being counted in 'Notes' view ([#2062](https://github.com/standardnotes/app/issues/2062)) ([2b0c9b1](https://github.com/standardnotes/app/commit/2b0c9b188cd75477c0c1611443daadb8ffa29306))
## [2.152.5](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-27)
**Note:** Version bump only for package @standardnotes/snjs
## [2.152.4](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-24)
### Bug Fixes
+15 -8
View File
@@ -64,7 +64,7 @@ import {
SessionStrings,
AccountEvent,
} from '@standardnotes/services'
import { FilesClientInterface } from '@standardnotes/files'
import { BackupServiceInterface, FilesClientInterface } from '@standardnotes/files'
import { ComputePrivateUsername } from '@standardnotes/encryption'
import { useBoolean } from '@standardnotes/utils'
import {
@@ -275,7 +275,7 @@ export class SNApplication implements ApplicationInterface, AppGroupManagedAppli
return this.statusService
}
public get fileBackups(): FilesBackupService | undefined {
public get fileBackups(): BackupServiceInterface | undefined {
return this.filesBackupService
}
@@ -295,6 +295,10 @@ export class SNApplication implements ApplicationInterface, AppGroupManagedAppli
return this.diskStorageService
}
public get actions(): InternalServices.SNActionsService {
return this.actionsManager
}
public computePrivateUsername(username: string): Promise<string | undefined> {
return ComputePrivateUsername(this.options.crypto, username)
}
@@ -1109,16 +1113,17 @@ export class SNApplication implements ApplicationInterface, AppGroupManagedAppli
this.createComponentManager()
this.createMigrationService()
this.createMfaService()
this.createStatusService()
if (isDesktopDevice(this.deviceInterface)) {
this.createFilesBackupService(this.deviceInterface)
}
this.createFileService()
this.createIntegrityService()
this.createMutatorService()
this.createListedService()
this.createActionsManager()
this.createStatusService()
if (isDesktopDevice(this.deviceInterface)) {
this.createFilesBackupService(this.deviceInterface)
}
}
private clearServices() {
@@ -1206,6 +1211,7 @@ export class SNApplication implements ApplicationInterface, AppGroupManagedAppli
this.alertService,
this.options.crypto,
this.internalEventBus,
this.fileBackups,
)
this.services.push(this.fileService)
@@ -1379,7 +1385,7 @@ export class SNApplication implements ApplicationInterface, AppGroupManagedAppli
}
private createItemManager() {
this.itemManager = new InternalServices.ItemManager(this.payloadManager, this.options, this.internalEventBus)
this.itemManager = new InternalServices.ItemManager(this.payloadManager, this.internalEventBus)
this.services.push(this.itemManager)
}
@@ -1666,6 +1672,7 @@ export class SNApplication implements ApplicationInterface, AppGroupManagedAppli
this.protocolService,
device,
this.statusService,
this.options.crypto,
this.internalEventBus,
)
this.services.push(this.filesBackupService)
@@ -1,11 +1,9 @@
import { ApplicationDisplayOptions, ApplicationSyncOptions } from './OptionalOptions'
import { ApplicationSyncOptions } from './OptionalOptions'
export interface ApplicationOptionsWhichHaveDefaults {
loadBatchSize: ApplicationSyncOptions['loadBatchSize']
supportsFileNavigation: ApplicationDisplayOptions['supportsFileNavigation']
}
export const ApplicationOptionsDefaults: ApplicationOptionsWhichHaveDefaults = {
loadBatchSize: 700,
supportsFileNavigation: false,
}
@@ -5,9 +5,8 @@ export interface ApplicationSyncOptions {
loadBatchSize: number
}
export interface ApplicationDisplayOptions {
supportsFileNavigation: boolean
}
// eslint-disable-next-line @typescript-eslint/no-empty-interface
export interface ApplicationDisplayOptions {}
export interface ApplicationOptionalConfiguratioOptions {
/**
@@ -1,3 +1,4 @@
import { removeFromArray } from '@standardnotes/utils'
import { SNRootKey } from '@standardnotes/encryption'
import { ChallengeService } from '../Challenge'
import { ListedService } from '../Listed/ListedService'
@@ -18,6 +19,8 @@ import {
isErrorDecryptingPayload,
CreateEncryptedBackupFileContextPayload,
EncryptedTransferPayload,
TransferPayload,
ItemContent,
} from '@standardnotes/models'
import { SNSyncService } from '../Sync/SyncService'
import { PayloadManager } from '../Payloads/PayloadManager'
@@ -34,6 +37,8 @@ import {
Challenge,
} from '@standardnotes/services'
type PayloadRequestHandler = (uuid: string) => TransferPayload | undefined
/**
* The Actions Service allows clients to interact with action-based extensions.
* Action-based extensions are mostly RESTful actions that can push a local value or
@@ -50,6 +55,7 @@ import {
*/
export class SNActionsService extends AbstractService {
private previousPasswords: string[] = []
private payloadRequestHandlers: PayloadRequestHandler[] = []
constructor(
private itemManager: ItemManager,
@@ -77,10 +83,19 @@ export class SNActionsService extends AbstractService {
;(this.challengeService as unknown) = undefined
;(this.protocolService as unknown) = undefined
;(this.syncService as unknown) = undefined
this.payloadRequestHandlers.length = 0
this.previousPasswords.length = 0
super.deinit()
}
public addPayloadRequestHandler(handler: PayloadRequestHandler) {
this.payloadRequestHandlers.push(handler)
return () => {
removeFromArray(this.payloadRequestHandlers, handler)
}
}
public getExtensions(): SNActionsExtension[] {
const extensionItems = this.itemManager.getItems<SNActionsExtension>(ContentType.ActionsExtension)
const excludingListed = extensionItems.filter((extension) => !extension.isListedExtension)
@@ -312,7 +327,16 @@ export class SNActionsService extends AbstractService {
return {} as ActionResponse
}
private async outgoingPayloadForItem(item: DecryptedItemInterface, decrypted = false) {
private async outgoingPayloadForItem(
item: DecryptedItemInterface,
decrypted = false,
): Promise<TransferPayload<ItemContent>> {
const payloadFromHandler = this.getPayloadFromRequestHandlers(item.uuid)
if (payloadFromHandler) {
return payloadFromHandler
}
if (decrypted) {
return item.payload.ejected()
}
@@ -323,4 +347,15 @@ export class SNActionsService extends AbstractService {
return CreateEncryptedBackupFileContextPayload(encrypted)
}
private getPayloadFromRequestHandlers(uuid: string): TransferPayload | undefined {
for (const handler of this.payloadRequestHandlers) {
const payload = handler(uuid)
if (payload) {
return payload
}
}
return undefined
}
}
@@ -197,7 +197,7 @@ describe('featuresService', () => {
describe('loadUserRoles()', () => {
it('retrieves user roles and features from storage', async () => {
await createService().initializeFromDisk()
createService().initializeFromDisk()
expect(storageService.getValue).toHaveBeenCalledWith(StorageKey.UserRoles, undefined, [])
expect(storageService.getValue).toHaveBeenCalledWith(StorageKey.UserFeatures, undefined, [])
})
@@ -576,6 +576,14 @@ describe('featuresService', () => {
expect(featuresService.getFeatureStatus(FeatureIdentifier.SheetsEditor)).toBe(FeatureStatus.NotInCurrentPlan)
})
it('availableInRoles-based features', async () => {
const featuresService = createService()
await featuresService.updateRolesAndFetchFeatures('123', [RoleName.ProUser])
expect(featuresService.getFeatureStatus(FeatureIdentifier.SuperEditor)).toBe(FeatureStatus.Entitled)
})
it('third party feature status', async () => {
const featuresService = createService()
@@ -506,13 +506,11 @@ export class SNFeaturesService
return FeatureStatus.Entitled
}
if (this.isExperimentalFeature(featureId)) {
const nativeFeature = FeaturesImports.FindNativeFeature(featureId)
if (nativeFeature) {
const hasRole = this.roles.some((role) => nativeFeature.availableInRoles?.includes(role))
if (hasRole) {
return FeatureStatus.Entitled
}
const nativeFeature = FeaturesImports.FindNativeFeature(featureId)
if (nativeFeature && nativeFeature.availableInRoles) {
const hasRole = this.roles.some((role) => nativeFeature.availableInRoles?.includes(role))
if (hasRole) {
return FeatureStatus.Entitled
}
}
@@ -525,7 +523,7 @@ export class SNFeaturesService
}
}
const isThirdParty = FeaturesImports.FindNativeFeature(featureId) == undefined
const isThirdParty = nativeFeature == undefined
if (isThirdParty) {
const component = this.itemManager
.getDisplayableComponents()
@@ -49,7 +49,7 @@ describe('itemManager', () => {
let internalEventBus: InternalEventBusInterface
const createService = () => {
return new ItemManager(payloadManager, { supportsFileNavigation: false }, internalEventBus)
return new ItemManager(payloadManager, internalEventBus)
}
beforeEach(() => {
@@ -8,7 +8,6 @@ import * as Models from '@standardnotes/models'
import * as Services from '@standardnotes/services'
import { PayloadManagerChangeData } from '../Payloads'
import { DiagnosticInfo, ItemsClientInterface, ItemRelationshipDirection } from '@standardnotes/services'
import { ApplicationDisplayOptions } from '@Lib/Application/Options/OptionalOptions'
import { CollectionSort, DecryptedItemInterface, ItemContent, SmartViewDefaultIconName } from '@standardnotes/models'
type ItemsChangeObserver<I extends Models.DecryptedItemInterface = Models.DecryptedItemInterface> = {
@@ -44,7 +43,6 @@ export class ItemManager
constructor(
private payloadManager: PayloadManager,
private readonly options: ApplicationDisplayOptions = { supportsFileNavigation: false },
protected override internalEventBus: Services.InternalEventBusInterface,
) {
super(internalEventBus)
@@ -55,7 +53,7 @@ export class ItemManager
}
private rebuildSystemSmartViews(criteria: Models.FilterDisplayOptions): Models.SmartView[] {
this.systemSmartViews = Models.BuildSmartViews(criteria, this.options)
this.systemSmartViews = Models.BuildSmartViews(criteria)
return this.systemSmartViews
}
@@ -68,7 +66,7 @@ export class ItemManager
{
sortBy: 'created_at',
sortDirection: 'dsc',
hiddenContentTypes: !this.options.supportsFileNavigation ? [ContentType.File] : [],
hiddenContentTypes: [],
},
)
this.tagDisplayController = new Models.ItemDisplayController(this.collection, [ContentType.Tag], {
@@ -184,12 +182,7 @@ export class ItemManager
public getDisplayableNotes(): Models.SNNote[] {
assert(this.navigationDisplayController.contentTypes.length === 2)
const fileContentTypeHidden = !this.options.supportsFileNavigation
if (fileContentTypeHidden) {
return this.navigationDisplayController.items() as Models.SNNote[]
} else {
return this.navigationDisplayController.items().filter(Models.isNote)
}
return this.navigationDisplayController.items().filter(Models.isNote)
}
public getDisplayableFiles(): Models.FileItem[] {
@@ -197,7 +190,6 @@ export class ItemManager
}
public getDisplayableNotesAndFiles(): (Models.SNNote | Models.FileItem)[] {
assert(this.options.supportsFileNavigation)
return this.navigationDisplayController.items()
}
@@ -215,7 +207,6 @@ export class ItemManager
public override deinit(): void {
this.unsubChangeObserver()
;(this.options as unknown) = undefined
;(this.unsubChangeObserver as unknown) = undefined
;(this.payloadManager as unknown) = undefined
;(this.collection as unknown) = undefined
@@ -289,13 +280,17 @@ export class ItemManager
}
public allCountableNotesCount(): number {
return this.tagItemsIndex.allCountableItemsCount()
return this.tagItemsIndex.allCountableNotesCount()
}
public allCountableFilesCount(): number {
return this.tagItemsIndex.allCountableFilesCount()
}
public countableNotesForTag(tag: Models.SNTag | Models.SmartView): number {
if (tag instanceof Models.SmartView) {
if (tag.uuid === Models.SystemViewId.AllNotes) {
return this.tagItemsIndex.allCountableItemsCount()
return this.tagItemsIndex.allCountableNotesCount()
}
throw Error('countableItemsForTag is not meant to be used for smart views.')
@@ -36,7 +36,7 @@ describe('mutator service', () => {
internalEventBus.publish = jest.fn()
payloadManager = new PayloadManager(internalEventBus)
itemManager = new ItemManager(payloadManager, { supportsFileNavigation: false }, internalEventBus)
itemManager = new ItemManager(payloadManager, internalEventBus)
mutatorService = new MutatorService(
itemManager,
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/snjs",
"version": "2.152.4",
"version": "2.156.0",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
@@ -28,7 +28,7 @@
"lint:eslint": "eslint --ext .ts lib/",
"lint:fix": "eslint --fix --ext .ts lib/",
"lint:tsc": "tsc --noEmit --emitDeclarationOnly false --project lib/tsconfig.json",
"test": "jest spec --coverage",
"test": "jest --coverage",
"test:debug": "node --inspect-brk node_modules/.bin/jest --runInBand"
},
"devDependencies": {
+4
View File
@@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.3.12](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-28)
**Note:** Version bump only for package @standardnotes/toast
## [1.3.11](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-19)
**Note:** Version bump only for package @standardnotes/toast
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/toast",
"version": "1.3.11",
"version": "1.3.12",
"private": true,
"main": "./src/index.ts",
"scripts": {
+66
View File
@@ -3,6 +3,72 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.19.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-05)
**Note:** Version bump only for package @standardnotes/ui-services
# [1.19.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-05)
### Features
* Added keyboard shortcut to toggle dark theme (Ctrl/Cmd+Shift+D) ([e68c708](https://github.com/standardnotes/app/commit/e68c7086282c5acaaacdfd0030148f1152b98842))
# [1.18.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-05)
### Features
* Added keyboard shortcut (Ctrl/Cmd+,) for opening preferences ([7c261af](https://github.com/standardnotes/app/commit/7c261af23018c398d24d695fb024df83928b8189))
## [1.17.11](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-04)
**Note:** Version bump only for package @standardnotes/ui-services
## [1.17.10](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-02)
**Note:** Version bump only for package @standardnotes/ui-services
## [1.17.9](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-02)
**Note:** Version bump only for package @standardnotes/ui-services
## [1.17.8](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-02)
**Note:** Version bump only for package @standardnotes/ui-services
## [1.17.7](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-01)
**Note:** Version bump only for package @standardnotes/ui-services
## [1.17.6](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-12-01)
**Note:** Version bump only for package @standardnotes/ui-services
## [1.17.5](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-28)
**Note:** Version bump only for package @standardnotes/ui-services
## [1.17.4](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-28)
**Note:** Version bump only for package @standardnotes/ui-services
## [1.17.3](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-28)
**Note:** Version bump only for package @standardnotes/ui-services
## [1.17.2](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-28)
**Note:** Version bump only for package @standardnotes/ui-services
## [1.17.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-27)
**Note:** Version bump only for package @standardnotes/ui-services
# [1.17.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-25)
### Features
* Markdown, HTML & JSON export options for super notes ([#2054](https://github.com/standardnotes/app/issues/2054)) ([dcc8cfb](https://github.com/standardnotes/app/commit/dcc8cfbe45644dbb77e0ade0073017e6ac66164c))
# [1.16.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-11-24)
### Features
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/ui-services",
"version": "1.16.0",
"version": "1.19.1",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
@@ -2,8 +2,8 @@ import { KeyboardCommand } from './KeyboardCommands'
export type KeyboardCommandHandler = {
command: KeyboardCommand
onKeyDown?: (event: KeyboardEvent) => boolean | void
onKeyUp?: (event: KeyboardEvent) => boolean | void
onKeyDown?: (event: KeyboardEvent, data?: unknown) => boolean | void
onKeyUp?: (event: KeyboardEvent, data?: unknown) => boolean | void
element?: HTMLElement
elements?: HTMLElement[]
notElement?: HTMLElement
@@ -17,6 +17,7 @@ export const DELETE_NOTE_KEYBOARD_COMMAND = createKeyboardCommand('DELETE_NOTE_K
export const TAB_COMMAND = createKeyboardCommand('PLAIN_EDITOR_INSERT_TAB_KEYBOARD_COMMAND')
export const ESCAPE_COMMAND = createKeyboardCommand('ESCAPE_COMMAND')
export const TOGGLE_FOCUS_MODE_COMMAND = createKeyboardCommand('TOGGLE_FOCUS_MODE_COMMAND')
export const TOGGLE_DARK_MODE_COMMAND = createKeyboardCommand('TOGGLE_DARK_MODE_COMMAND')
export const CHANGE_EDITOR_COMMAND = createKeyboardCommand('CHANGE_EDITOR_COMMAND')
export const FOCUS_TAGS_INPUT_COMMAND = createKeyboardCommand('FOCUS_TAGS_INPUT_COMMAND')
export const CREATE_NEW_TAG_COMMAND = createKeyboardCommand('CREATE_NEW_TAG_COMMAND')
@@ -25,3 +26,7 @@ export const CAPTURE_SAVE_COMMAND = createKeyboardCommand('CAPTURE_SAVE_COMMAND'
export const STAR_NOTE_COMMAND = createKeyboardCommand('STAR_NOTE_COMMAND')
export const PIN_NOTE_COMMAND = createKeyboardCommand('PIN_NOTE_COMMAND')
export const SUPER_SHOW_MARKDOWN_PREVIEW = createKeyboardCommand('SUPER_SHOW_MARKDOWN_PREVIEW')
export const SUPER_EXPORT_JSON = createKeyboardCommand('SUPER_EXPORT_JSON')
export const SUPER_EXPORT_MARKDOWN = createKeyboardCommand('SUPER_EXPORT_MARKDOWN')
export const SUPER_EXPORT_HTML = createKeyboardCommand('SUPER_EXPORT_HTML')
export const OPEN_PREFERENCES_COMMAND = createKeyboardCommand('OPEN_PREFERENCES_COMMAND')
@@ -165,7 +165,7 @@ export class KeyboardService {
}
}
public triggerCommand(command: KeyboardCommand): void {
public triggerCommand(command: KeyboardCommand, data?: unknown): void {
for (const observer of this.commandHandlers) {
if (observer.command !== command) {
continue
@@ -173,7 +173,7 @@ export class KeyboardService {
const callback = observer.onKeyDown || observer.onKeyUp
if (callback) {
const exclusive = callback(new KeyboardEvent('command-trigger'))
const exclusive = callback(new KeyboardEvent('command-trigger'), data)
if (exclusive) {
return
}
@@ -22,6 +22,8 @@ import {
STAR_NOTE_COMMAND,
PIN_NOTE_COMMAND,
SUPER_SHOW_MARKDOWN_PREVIEW,
OPEN_PREFERENCES_COMMAND,
TOGGLE_DARK_MODE_COMMAND,
} from './KeyboardCommands'
import { KeyboardKey } from './KeyboardKey'
import { KeyboardModifier } from './KeyboardModifier'
@@ -92,6 +94,12 @@ export function getKeyboardShortcuts(platform: Platform, _environment: Environme
key: 'f',
modifiers: [primaryModifier, KeyboardModifier.Shift],
},
{
command: TOGGLE_DARK_MODE_COMMAND,
key: 'd',
modifiers: [primaryModifier, KeyboardModifier.Shift],
preventDefault: true,
},
{
command: CHANGE_EDITOR_COMMAND,
key: '/',
@@ -139,5 +147,11 @@ export function getKeyboardShortcuts(platform: Platform, _environment: Environme
modifiers: [primaryModifier, KeyboardModifier.Shift],
preventDefault: true,
},
{
command: OPEN_PREFERENCES_COMMAND,
key: ',',
modifiers: [primaryModifier],
preventDefault: true,
},
]
}
@@ -7,3 +7,7 @@ export function isMacPlatform(platform: Platform) {
export function isWindowsPlatform(platform: Platform) {
return platform === Platform.WindowsDesktop || platform === Platform.WindowsWeb
}
export function isMobilePlatform(platform: Platform) {
return platform === Platform.Ios || platform === Platform.Android
}

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