Compare commits

...
Author SHA1 Message Date
StandardNotes CI 168880eec6 chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
2023-09-28 16:23:45 +00:00
Karol Sójko 94de5b73db chore: skip test to allow server changes in behavior 2023-09-28 14:34:59 +02:00
Karol Sójko 4b1140d8f0 chore: fix getting vault method (#2547)
* chore: fix getting vault method

* remove only attribute

* fix checking for vaults

* fix expected missing vaults
2023-09-28 14:18:48 +02:00
Aman Harwara 397d33285c chore: update delegated contacts test 2023-09-27 18:27:51 +05:30
Karol Sójko 981417f952 chore: designated survivor test suite (#2540)
* chore: designated survivor test suite

* add more tests

* fix: tests exclusivness

* fix removing account from shared vaults test
2023-09-27 14:27:12 +02:00
StandardNotes CI 70cc15c6d9 chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
2023-09-26 14:02:07 +00:00
Aman Harwara 9de994bcba chore: add tests for moving items into vault (#2539) 2023-09-26 18:37:17 +05:30
Mo 896bd1eda7 chore: add test for invalid content type duplication 2023-09-26 07:48:59 -05:00
StandardNotes CI 487f3e7b57 chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
2023-09-26 12:36:13 +00:00
Karol Sójko 2ce908da29 chore: add method to designate a shared vault survivor (#2538) 2023-09-26 13:48:36 +02:00
Aman Harwara 60f96bc201 chore: handle moving & removing tags with subtags into/out of vaults (#2528) 2023-09-26 16:40:09 +05:30
Karol Sójko fe0cd96883 chore: re-enable history tests (#2536) 2023-09-26 12:31:44 +02:00
Aman Harwara b2ead8fc84 chore: handle notification on guest when host cancels invite (#2537) 2023-09-26 15:53:18 +05:30
Karol Sójko 3777a5389e chore: refactor notifications and re-enable vault tests (#2532)
* chore: refactor notifications and re-enable vault tests

* fix message in error expected
2023-09-26 10:21:26 +02:00
StandardNotes CI 192a2eccb8 chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
2023-09-26 07:58:07 +00:00
Aman Harwara 2a27e512fb chore: fix conflicts filter 2023-09-26 12:57:42 +05:30
StandardNotes CI 4aa7314eef chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
2023-09-25 13:35:49 +00:00
Karol Sójko bd803aed0c chore: disable vault tests to allow notifications refactor 2023-09-25 15:05:59 +02:00
StandardNotes CI 7364d6b0e0 chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
2023-09-22 14:54:11 +00:00
Aman Harwara 9fa52d9039 chore: fix note view error [skip e2e] 2023-09-22 20:08:35 +05:30
Aman Harwara e357a7820c chore: allow viewing note history as readonly member 2023-09-22 19:48:49 +05:30
StandardNotes CI ab1699c114 chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
2023-09-22 14:03:16 +00:00
Karol Sójko c084dcd11e chore: enable vault tests (#2518) 2023-09-22 15:09:45 +02:00
StandardNotes CI 41ae1dae83 chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
2023-09-22 11:31:32 +00:00
Karol Sójko f7d204b205 chore: add designated survivor uuid to shared vault metadata (#2527)
* chore: add designated survivor uuid to shared vault metadata

* fix specs
2023-09-22 12:54:42 +02:00
StandardNotes CI 2c599df6be chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
2023-09-22 08:11:18 +00:00
Aman Harwara 80711a17fa chore: handle notification of another member being removed (#2525) 2023-09-22 13:10:53 +05:30
StandardNotes CI 07d18a6e9a chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
2023-09-21 17:32:30 +00:00
Aman Harwara 6839ad9e5a chore: clear multiple selection if selected items are not supposed to be in the current view anymore 2023-09-21 22:27:03 +05:30
StandardNotes CI 1defde14fe chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
2023-09-21 16:48:34 +00:00
Aman Harwara febaf5185d chore: when creating new tag from link input, move it to the vault the item is in before linking 2023-09-21 21:46:46 +05:30
StandardNotes CI d55d0eb806 chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
2023-09-21 15:26:24 +00:00
Aman Harwara 6530aa4c79 chore: hide conflicts button if note is readonly 2023-09-21 20:24:29 +05:30
Aman Harwara e36c36f401 chore: fix conflicts count not taking into account exclusive vault mode (#2505) 2023-09-21 20:04:05 +05:30
StandardNotes CI 40d45d2484 chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].9
2023-09-21 08:33:38 +00:00
Aman Harwara bd1310b1fc chore: show alert when deleting contact belonging to an owned vault 2023-09-21 13:29:16 +05:30
StandardNotes CI 062e50e901 chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].9
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].8
2023-09-20 15:50:55 +00:00
Aman Harwara ec61901aa5 fix: Fixed issue where using table or image option in Super mobile toolbar would make uninteractive 2023-09-20 20:52:01 +05:30
Karol Sójko 0d15858b5f chore: fix notification type on shared vault user removal (#2521) 2023-09-20 17:01:07 +02:00
StandardNotes CI 09d139846e chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].7
2023-09-20 13:40:09 +00:00
Karol Sójko da234f960e chore: skip still flaky history tests (#2520) 2023-09-20 15:10:23 +02:00
StandardNotes CI f7c2434141 chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].6
2023-09-20 12:25:08 +00:00
Aman Harwara e2901d9535 chore: only invalidate vault user cache when required (#2519) 2023-09-20 17:20:30 +05:30
StandardNotes CI 3a42925ca2 chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].5
2023-09-20 10:25:37 +00:00
Karol Sójko 3e1557ab36 chore: fix history test with additional await if needed (#2517) 2023-09-20 11:50:15 +02:00
StandardNotes CI 9e1583f798 chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].4
2023-09-19 19:23:41 +00:00
Aman Harwara 35d92be63c chore: update vaults preferences 2023-09-20 00:22:34 +05:30
Karol Sójko 3b61fa5911 chore: disabling history tests as they are still flaky 2023-09-19 15:13:19 +02:00
StandardNotes CI 0f046cbe83 chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].3
2023-09-19 06:04:39 +00:00
Karol Sójko 102d9c8e2d chore: re-enable flaky history tests (#2510) 2023-09-19 07:33:58 +02:00
StandardNotes CI ca01b2dc95 chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].2
2023-09-18 17:39:29 +00:00
Aman Harwara dbf918523c chore: show account update prompt in vaults preferences if account protocol is older than 004 2023-09-18 22:39:24 +05:30
99 changed files with 2454 additions and 389 deletions
+20
View File
@@ -3,6 +3,26 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.26.61](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-28)
**Note:** Version bump only for package @standardnotes/api
## [1.26.60](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-26)
**Note:** Version bump only for package @standardnotes/api
## [1.26.59](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-22)
**Note:** Version bump only for package @standardnotes/api
## [1.26.58](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-21)
**Note:** Version bump only for package @standardnotes/api
## [1.26.57](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-20)
**Note:** Version bump only for package @standardnotes/api
## [1.26.56](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-13)
**Note:** Version bump only for package @standardnotes/api
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/api",
"version": "1.26.56",
"version": "1.26.61",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
@@ -36,7 +36,7 @@
},
"dependencies": {
"@standardnotes/common": "^1.50.0",
"@standardnotes/domain-core": "^1.28.0",
"@standardnotes/domain-core": "^1.34.1",
"@standardnotes/models": "workspace:*",
"@standardnotes/responses": "workspace:*",
"@standardnotes/utils": "workspace:*",
@@ -2,4 +2,8 @@ import { SharedVaultServerHash } from '@standardnotes/responses'
export type GetSharedVaultsResponse = {
sharedVaults: SharedVaultServerHash[]
designatedSurvivors: Array<{
userUuid: string
sharedVaultUuid: string
}>
}
@@ -0,0 +1,3 @@
export type DesignateSurvivorResponse = {
success: boolean
}
@@ -2,4 +2,6 @@ export const SharedVaultUsersPaths = {
getSharedVaultUsers: (sharedVaultUuid: string) => `/v1/shared-vaults/${sharedVaultUuid}/users`,
deleteSharedVaultUser: (sharedVaultUuid: string, userUuid: string) =>
`/v1/shared-vaults/${sharedVaultUuid}/users/${userUuid}`,
designateSurvivor: (sharedVaultUuid: string, sharedVaultMemberUuid: string) =>
`/v1/shared-vaults/${sharedVaultUuid}/users/${sharedVaultMemberUuid}/designate-survivor`,
}
@@ -1,4 +1,6 @@
import { HttpResponse } from '@standardnotes/responses'
import { Uuid } from '@standardnotes/domain-core'
import { HttpServiceInterface } from '../../Http'
import { GetSharedVaultUsersRequestParams } from '../../Request/SharedVaultUser/GetSharedVaultUsersRequestParams'
import { DeleteSharedVaultUserRequestParams } from '../../Request/SharedVaultUser/DeleteSharedVaultUserRequestParams'
@@ -6,10 +8,20 @@ import { DeleteSharedVaultUserResponse } from '../../Response/SharedVaultUsers/D
import { SharedVaultUsersServerInterface } from './SharedVaultUsersServerInterface'
import { SharedVaultUsersPaths } from './Paths'
import { GetSharedVaultUsersResponse } from '../../Response/SharedVaultUsers/GetSharedVaultUsersResponse'
import { DesignateSurvivorResponse } from '../../Response/SharedVaultUsers/DesignateSurvivorResponse'
export class SharedVaultUsersServer implements SharedVaultUsersServerInterface {
constructor(private httpService: HttpServiceInterface) {}
async designateSurvivor(params: {
sharedVaultUuid: Uuid
sharedVaultMemberUuid: Uuid
}): Promise<HttpResponse<DesignateSurvivorResponse>> {
return this.httpService.post(
SharedVaultUsersPaths.designateSurvivor(params.sharedVaultUuid.value, params.sharedVaultMemberUuid.value),
)
}
getSharedVaultUsers(params: GetSharedVaultUsersRequestParams): Promise<HttpResponse<GetSharedVaultUsersResponse>> {
return this.httpService.get(SharedVaultUsersPaths.getSharedVaultUsers(params.sharedVaultUuid))
}
@@ -1,12 +1,18 @@
import { HttpResponse } from '@standardnotes/responses'
import { Uuid } from '@standardnotes/domain-core'
import { GetSharedVaultUsersRequestParams } from '../../Request/SharedVaultUser/GetSharedVaultUsersRequestParams'
import { DeleteSharedVaultUserRequestParams } from '../../Request/SharedVaultUser/DeleteSharedVaultUserRequestParams'
import { DeleteSharedVaultUserResponse } from '../../Response/SharedVaultUsers/DeleteSharedVaultUserResponse'
import { GetSharedVaultUsersResponse } from '../../Response/SharedVaultUsers/GetSharedVaultUsersResponse'
import { DesignateSurvivorResponse } from '../../Response/SharedVaultUsers/DesignateSurvivorResponse'
export interface SharedVaultUsersServerInterface {
getSharedVaultUsers(params: GetSharedVaultUsersRequestParams): Promise<HttpResponse<GetSharedVaultUsersResponse>>
designateSurvivor(params: {
sharedVaultUuid: Uuid
sharedVaultMemberUuid: Uuid
}): Promise<HttpResponse<DesignateSurvivorResponse>>
deleteSharedVaultUser(
params: DeleteSharedVaultUserRequestParams,
): Promise<HttpResponse<DeleteSharedVaultUserResponse>>
+80
View File
@@ -3,6 +3,86 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.1.247](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-28)
**Note:** Version bump only for package @standardnotes/clipper
## [1.1.246](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-26)
**Note:** Version bump only for package @standardnotes/clipper
## [1.1.245](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-26)
**Note:** Version bump only for package @standardnotes/clipper
## [1.1.244](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-26)
**Note:** Version bump only for package @standardnotes/clipper
## [1.1.243](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-25)
**Note:** Version bump only for package @standardnotes/clipper
## [1.1.242](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-22)
**Note:** Version bump only for package @standardnotes/clipper
## [1.1.241](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-22)
**Note:** Version bump only for package @standardnotes/clipper
## [1.1.240](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-22)
**Note:** Version bump only for package @standardnotes/clipper
## [1.1.239](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-22)
**Note:** Version bump only for package @standardnotes/clipper
## [1.1.238](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-21)
**Note:** Version bump only for package @standardnotes/clipper
## [1.1.237](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-21)
**Note:** Version bump only for package @standardnotes/clipper
## [1.1.236](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-21)
**Note:** Version bump only for package @standardnotes/clipper
## [1.1.235](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-21)
**Note:** Version bump only for package @standardnotes/clipper
## [1.1.234](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-20)
**Note:** Version bump only for package @standardnotes/clipper
## [1.1.233](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-20)
**Note:** Version bump only for package @standardnotes/clipper
## [1.1.232](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-20)
**Note:** Version bump only for package @standardnotes/clipper
## [1.1.231](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-20)
**Note:** Version bump only for package @standardnotes/clipper
## [1.1.230](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-19)
**Note:** Version bump only for package @standardnotes/clipper
## [1.1.229](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-19)
**Note:** Version bump only for package @standardnotes/clipper
## [1.1.228](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-18)
**Note:** Version bump only for package @standardnotes/clipper
## [1.1.227](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-18)
**Note:** Version bump only for package @standardnotes/clipper
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "@standardnotes/clipper",
"description": "Web clipper browser extension for Standard Notes",
"version": "1.1.227",
"version": "1.1.247",
"private": true,
"scripts": {
"build-mv2": "yarn clean && webpack --config ./webpack.config.prod.js",
+80
View File
@@ -3,6 +3,86 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [3.108.179](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-28)
**Note:** Version bump only for package @standardnotes/desktop
## [3.108.178](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-26)
**Note:** Version bump only for package @standardnotes/desktop
## [3.108.177](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-26)
**Note:** Version bump only for package @standardnotes/desktop
## [3.108.176](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-26)
**Note:** Version bump only for package @standardnotes/desktop
## [3.108.175](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-25)
**Note:** Version bump only for package @standardnotes/desktop
## [3.108.174](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-22)
**Note:** Version bump only for package @standardnotes/desktop
## [3.108.173](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-22)
**Note:** Version bump only for package @standardnotes/desktop
## [3.108.172](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-22)
**Note:** Version bump only for package @standardnotes/desktop
## [3.108.171](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-22)
**Note:** Version bump only for package @standardnotes/desktop
## [3.108.170](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-21)
**Note:** Version bump only for package @standardnotes/desktop
## [3.108.169](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-21)
**Note:** Version bump only for package @standardnotes/desktop
## [3.108.168](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-21)
**Note:** Version bump only for package @standardnotes/desktop
## [3.108.167](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-21)
**Note:** Version bump only for package @standardnotes/desktop
## [3.108.166](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-20)
**Note:** Version bump only for package @standardnotes/desktop
## [3.108.165](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-20)
**Note:** Version bump only for package @standardnotes/desktop
## [3.108.164](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-20)
**Note:** Version bump only for package @standardnotes/desktop
## [3.108.163](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-20)
**Note:** Version bump only for package @standardnotes/desktop
## [3.108.162](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-19)
**Note:** Version bump only for package @standardnotes/desktop
## [3.108.161](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-19)
**Note:** Version bump only for package @standardnotes/desktop
## [3.108.160](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-18)
**Note:** Version bump only for package @standardnotes/desktop
## [3.108.159](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-18)
**Note:** Version bump only for package @standardnotes/desktop
+2 -2
View File
@@ -1,7 +1,7 @@
{
"name": "@standardnotes/desktop",
"main": "./app/dist/index.js",
"version": "3.108.159",
"version": "3.108.179",
"license": "CC BY-NC-SA 4.0",
"author": "Standard Notes.",
"private": true,
@@ -35,7 +35,7 @@
},
"dependencies": {
"@electron/remote": "^2.0.9",
"@standardnotes/domain-core": "^1.28.0",
"@standardnotes/domain-core": "^1.34.1",
"@standardnotes/electron-clear-data": "1.1.1",
"@standardnotes/web": "workspace:*",
"axios": "^1.1.3",
+20
View File
@@ -3,6 +3,26 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.21.83](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-28)
**Note:** Version bump only for package @standardnotes/encryption
## [1.21.82](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-26)
**Note:** Version bump only for package @standardnotes/encryption
## [1.21.81](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-22)
**Note:** Version bump only for package @standardnotes/encryption
## [1.21.80](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-21)
**Note:** Version bump only for package @standardnotes/encryption
## [1.21.79](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-20)
**Note:** Version bump only for package @standardnotes/encryption
## [1.21.78](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-13)
**Note:** Version bump only for package @standardnotes/encryption
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/encryption",
"version": "1.21.78",
"version": "1.21.83",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
@@ -29,7 +29,7 @@
},
"dependencies": {
"@standardnotes/common": "^1.50.0",
"@standardnotes/domain-core": "^1.28.0",
"@standardnotes/domain-core": "^1.34.1",
"@standardnotes/models": "workspace:*",
"@standardnotes/responses": "workspace:*",
"@standardnotes/sncrypto-common": "workspace:*",
+12
View File
@@ -3,6 +3,18 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.59.21](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-28)
**Note:** Version bump only for package @standardnotes/features
## [1.59.20](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-26)
**Note:** Version bump only for package @standardnotes/features
## [1.59.19](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-20)
**Note:** Version bump only for package @standardnotes/features
## [1.59.18](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-07)
**Note:** Version bump only for package @standardnotes/features
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/features",
"version": "1.59.18",
"version": "1.59.21",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
@@ -26,7 +26,7 @@
},
"dependencies": {
"@standardnotes/common": "^1.50.0",
"@standardnotes/domain-core": "^1.28.0",
"@standardnotes/domain-core": "^1.34.1",
"reflect-metadata": "^0.1.13"
},
"devDependencies": {
+20
View File
@@ -3,6 +3,26 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.28.94](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-28)
**Note:** Version bump only for package @standardnotes/filepicker
## [1.28.93](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-26)
**Note:** Version bump only for package @standardnotes/filepicker
## [1.28.92](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-22)
**Note:** Version bump only for package @standardnotes/filepicker
## [1.28.91](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-21)
**Note:** Version bump only for package @standardnotes/filepicker
## [1.28.90](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-20)
**Note:** Version bump only for package @standardnotes/filepicker
## [1.28.89](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-13)
**Note:** Version bump only for package @standardnotes/filepicker
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/filepicker",
"version": "1.28.89",
"version": "1.28.94",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
+20
View File
@@ -3,6 +3,26 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.17.13](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-28)
**Note:** Version bump only for package @standardnotes/files
## [1.17.12](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-26)
**Note:** Version bump only for package @standardnotes/files
## [1.17.11](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-22)
**Note:** Version bump only for package @standardnotes/files
## [1.17.10](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-21)
**Note:** Version bump only for package @standardnotes/files
## [1.17.9](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-20)
**Note:** Version bump only for package @standardnotes/files
## [1.17.8](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-13)
**Note:** Version bump only for package @standardnotes/files
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/files",
"version": "1.17.8",
"version": "1.17.13",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
+80
View File
@@ -3,6 +3,86 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [3.56.158](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-28)
**Note:** Version bump only for package @standardnotes/mobile
## [3.56.157](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-26)
**Note:** Version bump only for package @standardnotes/mobile
## [3.56.156](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-26)
**Note:** Version bump only for package @standardnotes/mobile
## [3.56.155](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-26)
**Note:** Version bump only for package @standardnotes/mobile
## [3.56.154](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-25)
**Note:** Version bump only for package @standardnotes/mobile
## [3.56.153](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-22)
**Note:** Version bump only for package @standardnotes/mobile
## [3.56.152](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-22)
**Note:** Version bump only for package @standardnotes/mobile
## [3.56.151](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-22)
**Note:** Version bump only for package @standardnotes/mobile
## [3.56.150](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-22)
**Note:** Version bump only for package @standardnotes/mobile
## [3.56.149](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-21)
**Note:** Version bump only for package @standardnotes/mobile
## [3.56.148](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-21)
**Note:** Version bump only for package @standardnotes/mobile
## [3.56.147](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-21)
**Note:** Version bump only for package @standardnotes/mobile
## [3.56.146](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-21)
**Note:** Version bump only for package @standardnotes/mobile
## [3.56.145](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-20)
**Note:** Version bump only for package @standardnotes/mobile
## [3.56.144](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-20)
**Note:** Version bump only for package @standardnotes/mobile
## [3.56.143](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-20)
**Note:** Version bump only for package @standardnotes/mobile
## [3.56.142](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-20)
**Note:** Version bump only for package @standardnotes/mobile
## [3.56.141](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-19)
**Note:** Version bump only for package @standardnotes/mobile
## [3.56.140](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-19)
**Note:** Version bump only for package @standardnotes/mobile
## [3.56.139](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-18)
**Note:** Version bump only for package @standardnotes/mobile
## [3.56.138](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-18)
**Note:** Version bump only for package @standardnotes/mobile
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/mobile",
"version": "3.56.138",
"version": "3.56.158",
"author": "Standard Notes.",
"private": true,
"license": "CC BY-NC-SA 4.0",
+20
View File
@@ -3,6 +3,26 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.47.19](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-28)
**Note:** Version bump only for package @standardnotes/models
## [1.47.18](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-26)
**Note:** Version bump only for package @standardnotes/models
## [1.47.17](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-22)
**Note:** Version bump only for package @standardnotes/models
## [1.47.16](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-21)
**Note:** Version bump only for package @standardnotes/models
## [1.47.15](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-20)
**Note:** Version bump only for package @standardnotes/models
## [1.47.14](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-13)
**Note:** Version bump only for package @standardnotes/models
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/models",
"version": "1.47.14",
"version": "1.47.19",
"publishConfig": {
"access": "public"
},
@@ -20,7 +20,7 @@
},
"dependencies": {
"@standardnotes/common": "^1.50.0",
"@standardnotes/domain-core": "^1.28.0",
"@standardnotes/domain-core": "^1.34.1",
"@standardnotes/features": "workspace:*",
"@standardnotes/responses": "workspace:*",
"@standardnotes/sncrypto-common": "workspace:^",
@@ -20,6 +20,7 @@ export type AsymmetricMessageSharedVaultInvite = {
description?: string
iconString: IconType | EmojiString
fileBytesUsed: number
designatedSurvivor: string | null
}
}
}
@@ -31,6 +31,10 @@ export class ItemDisplayController<I extends DisplayItem, O extends AnyDisplayOp
return this.sortedItems
}
public hasExclusiveVaultOptions(): boolean {
return this.vaultOptions ? !isExclusionaryOptionsValue(this.vaultOptions.getOptions()) : false
}
public getDisplayOptions(): DisplayControllerDisplayOptions & O {
return this.options
}
@@ -2,4 +2,5 @@ export type VaultListingSharingInfo = {
sharedVaultUuid: string
ownerUserUuid: string
fileBytesUsed: number
designatedSurvivor: string | null
}
+80
View File
@@ -3,6 +3,86 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.4.531](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-28)
**Note:** Version bump only for package @standardnotes/releases
## [1.4.530](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-26)
**Note:** Version bump only for package @standardnotes/releases
## [1.4.529](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-26)
**Note:** Version bump only for package @standardnotes/releases
## [1.4.528](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-26)
**Note:** Version bump only for package @standardnotes/releases
## [1.4.527](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-25)
**Note:** Version bump only for package @standardnotes/releases
## [1.4.526](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-22)
**Note:** Version bump only for package @standardnotes/releases
## [1.4.525](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-22)
**Note:** Version bump only for package @standardnotes/releases
## [1.4.524](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-22)
**Note:** Version bump only for package @standardnotes/releases
## [1.4.523](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-22)
**Note:** Version bump only for package @standardnotes/releases
## [1.4.522](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-21)
**Note:** Version bump only for package @standardnotes/releases
## [1.4.521](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-21)
**Note:** Version bump only for package @standardnotes/releases
## [1.4.520](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-21)
**Note:** Version bump only for package @standardnotes/releases
## [1.4.519](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-21)
**Note:** Version bump only for package @standardnotes/releases
## [1.4.518](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-20)
**Note:** Version bump only for package @standardnotes/releases
## [1.4.517](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-20)
**Note:** Version bump only for package @standardnotes/releases
## [1.4.516](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-20)
**Note:** Version bump only for package @standardnotes/releases
## [1.4.515](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-20)
**Note:** Version bump only for package @standardnotes/releases
## [1.4.514](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-19)
**Note:** Version bump only for package @standardnotes/releases
## [1.4.513](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-19)
**Note:** Version bump only for package @standardnotes/releases
## [1.4.512](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-18)
**Note:** Version bump only for package @standardnotes/releases
## [1.4.511](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-18)
**Note:** Version bump only for package @standardnotes/releases
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/releases",
"version": "1.4.511",
"version": "1.4.531",
"license": "CC BY-NC-SA 4.0",
"main": "dist/releases.json",
"types": "dist/index.d.ts",
+12
View File
@@ -3,6 +3,18 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.13.45](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-28)
**Note:** Version bump only for package @standardnotes/responses
## [1.13.44](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-26)
**Note:** Version bump only for package @standardnotes/responses
## [1.13.43](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-20)
**Note:** Version bump only for package @standardnotes/responses
## [1.13.42](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-07)
**Note:** Version bump only for package @standardnotes/responses
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/responses",
"version": "1.13.42",
"version": "1.13.45",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
+40
View File
@@ -3,6 +3,46 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.64.30](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-28)
**Note:** Version bump only for package @standardnotes/services
## [1.64.29](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-26)
**Note:** Version bump only for package @standardnotes/services
## [1.64.28](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-26)
**Note:** Version bump only for package @standardnotes/services
## [1.64.27](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-22)
**Note:** Version bump only for package @standardnotes/services
## [1.64.26](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-22)
**Note:** Version bump only for package @standardnotes/services
## [1.64.25](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-22)
**Note:** Version bump only for package @standardnotes/services
## [1.64.24](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-22)
**Note:** Version bump only for package @standardnotes/services
## [1.64.23](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-21)
**Note:** Version bump only for package @standardnotes/services
## [1.64.22](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-20)
**Note:** Version bump only for package @standardnotes/services
## [1.64.21](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-20)
**Note:** Version bump only for package @standardnotes/services
## [1.64.20](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-18)
**Note:** Version bump only for package @standardnotes/services
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/services",
"version": "1.64.20",
"version": "1.64.30",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
@@ -18,7 +18,7 @@
"dependencies": {
"@standardnotes/api": "workspace:^",
"@standardnotes/common": "^1.50.0",
"@standardnotes/domain-core": "^1.28.0",
"@standardnotes/domain-core": "^1.34.1",
"@standardnotes/encryption": "workspace:^",
"@standardnotes/features": "workspace:^",
"@standardnotes/files": "workspace:^",
@@ -39,6 +39,7 @@ describe('ProcessAcceptedVaultInvite', () => {
name: 'test-name',
iconString: 'safe-square',
fileBytesUsed: 0,
designatedSurvivor: null,
},
trustedContacts: [],
},
@@ -37,6 +37,7 @@ export class ProcessAcceptedVaultInvite {
sharedVaultUuid: sharedVaultUuid,
ownerUserUuid: ownerUuid,
fileBytesUsed: metadata.fileBytesUsed,
designatedSurvivor: metadata.designatedSurvivor,
},
}
@@ -116,6 +116,7 @@ export interface ItemManagerInterface extends AbstractService {
getNoteCount(): number
getDisplayableTags(): SNTag[]
getTagChildren(itemToLookupUuidFor: SNTag): SNTag[]
getDeepTagChildren(itemToLookupUuidFor: SNTag): SNTag[]
getTagParent(itemToLookupUuidFor: SNTag): SNTag | undefined
getDisplayableTagParent(itemToLookupUuidFor: SNTag): SNTag | undefined
isValidTagParent(parentTagToLookUpUuidFor: SNTag, childToLookUpUuidFor: SNTag): boolean
@@ -15,6 +15,7 @@ import { ItemManagerInterface } from '../Item/ItemManagerInterface'
import { SessionsClientInterface } from '../Session/SessionsClientInterface'
import { ContactPublicKeySetInterface, TrustedContactInterface } from '@standardnotes/models'
import { SyncLocalVaultsWithRemoteSharedVaults } from './UseCase/SyncLocalVaultsWithRemoteSharedVaults'
import { VaultUserServiceInterface } from '../VaultUser/VaultUserServiceInterface'
describe('SharedVaultService', () => {
let service: SharedVaultService
@@ -27,6 +28,8 @@ describe('SharedVaultService', () => {
const items = {} as jest.Mocked<ItemManagerInterface>
items.addObserver = jest.fn()
const vaultUsers = {} as jest.Mocked<VaultUserServiceInterface>
const session = {} as jest.Mocked<SessionsClientInterface>
const getVault = {} as jest.Mocked<GetVault>
const getOwnedVaults = {} as jest.Mocked<GetOwnedSharedVaults>
@@ -48,6 +51,7 @@ describe('SharedVaultService', () => {
service = new SharedVaultService(
items,
session,
vaultUsers,
syncLocalVaultsWithRemoteSharedVaults,
getVault,
getOwnedVaults,
@@ -33,6 +33,7 @@ import { HandleKeyPairChange } from '../Contacts/UseCase/HandleKeyPairChange'
import { FindContact } from '../Contacts/UseCase/FindContact'
import { GetOwnedSharedVaults } from './UseCase/GetOwnedSharedVaults'
import { SyncLocalVaultsWithRemoteSharedVaults } from './UseCase/SyncLocalVaultsWithRemoteSharedVaults'
import { VaultUserServiceInterface } from '../VaultUser/VaultUserServiceInterface'
export class SharedVaultService
extends AbstractService<SharedVaultServiceEvent, SharedVaultServiceEventPayload>
@@ -41,6 +42,7 @@ export class SharedVaultService
constructor(
private items: ItemManagerInterface,
private session: SessionsClientInterface,
private vaultUsers: VaultUserServiceInterface,
private _syncLocalVaultsWithRemoteSharedVaults: SyncLocalVaultsWithRemoteSharedVaults,
private _getVault: GetVault,
private _getOwnedSharedVaults: GetOwnedSharedVaults,
@@ -94,38 +96,56 @@ export class SharedVaultService
await this.handleNotification(event.payload as NotificationServiceEventPayload)
break
case SyncEvent.ReceivedRemoteSharedVaults:
void this.notifyEventSync(SharedVaultServiceEvent.SharedVaultStatusChanged)
void this.notifyEvent(SharedVaultServiceEvent.SharedVaultStatusChanged)
break
}
}
private async handleNotification(event: NotificationServiceEventPayload): Promise<void> {
switch (event.eventPayload.props.type.value) {
case NotificationType.TYPES.RemovedFromSharedVault: {
case NotificationType.TYPES.SelfRemovedFromSharedVault: {
const vault = this._getVault.execute<SharedVaultListingInterface>({
sharedVaultUuid: event.eventPayload.props.sharedVaultUuid.value,
sharedVaultUuid: event.eventPayload.props.primaryIdentifier.value,
})
if (!vault.isFailed()) {
await this._deleteThirdPartyVault.execute(vault.getValue())
}
break
}
case NotificationType.TYPES.UserRemovedFromSharedVault: {
const vaultOrError = this._getVault.execute<SharedVaultListingInterface>({
sharedVaultUuid: event.eventPayload.props.primaryIdentifier.value,
})
if (!vaultOrError.isFailed()) {
const vault = vaultOrError.getValue()
this.vaultUsers
.invalidateVaultUsersCache(event.eventPayload.props.primaryIdentifier.value)
.catch(console.error)
await this._syncLocalVaultsWithRemoteSharedVaults.execute([vault])
void this.notifyEvent(SharedVaultServiceEvent.SharedVaultStatusChanged)
}
break
}
case NotificationType.TYPES.SharedVaultItemRemoved: {
const item = this.items.findItem((event.eventPayload.props.itemUuid as Uuid).value)
const item = this.items.findItem((event.eventPayload.props.secondaryIdentifier as Uuid).value)
if (item) {
void this._discardItemsLocally.execute([item])
}
break
}
case NotificationType.TYPES.SharedVaultFileRemoved:
case NotificationType.TYPES.SharedVaultFileUploaded: {
case NotificationType.TYPES.SharedVaultFileUploaded:
case NotificationType.TYPES.UserDesignatedAsSurvivor: {
const vaultOrError = this._getVault.execute<SharedVaultListingInterface>({
sharedVaultUuid: event.eventPayload.props.sharedVaultUuid.value,
sharedVaultUuid: event.eventPayload.props.primaryIdentifier.value,
})
if (!vaultOrError.isFailed()) {
await this._syncLocalVaultsWithRemoteSharedVaults.execute([vaultOrError.getValue()])
void this.notifyEventSync(SharedVaultServiceEvent.SharedVaultStatusChanged)
void this.notifyEvent(SharedVaultServiceEvent.SharedVaultStatusChanged)
}
break
@@ -32,6 +32,7 @@ export class ConvertToSharedVault {
sharedVaultUuid: serverVaultHash.uuid,
ownerUserUuid: serverVaultHash.user_uuid,
fileBytesUsed: serverVaultHash.file_upload_bytes_used,
designatedSurvivor: null,
}
},
)
@@ -51,6 +51,7 @@ export class CreateSharedVault {
sharedVaultUuid: serverVaultHash.uuid,
ownerUserUuid: serverVaultHash.user_uuid,
fileBytesUsed: serverVaultHash.file_upload_bytes_used,
designatedSurvivor: null,
}
},
)
@@ -18,18 +18,22 @@ export class SyncLocalVaultsWithRemoteSharedVaults implements UseCaseInterface<v
}
const remoteVaults = remoteVaultsResponse.data.sharedVaults
const designatedSurvivors = remoteVaultsResponse.data.designatedSurvivors || []
for (const localVault of localVaults) {
if (!localVault.isSharedVaultListing()) {
continue
}
const remoteVault = remoteVaults.find((vault) => vault.uuid === localVault.sharing.sharedVaultUuid)
if (remoteVault) {
const designatedSurvivor = designatedSurvivors.find((survivor) => survivor.sharedVaultUuid === remoteVault.uuid)
await this.mutator.changeItem<VaultListingMutator, SharedVaultListingInterface>(localVault, (mutator) => {
/* istanbul ignore next */
mutator.sharing = {
sharedVaultUuid: remoteVault.uuid,
ownerUserUuid: remoteVault.user_uuid,
fileBytesUsed: remoteVault.file_upload_bytes_used,
designatedSurvivor: designatedSurvivor ? designatedSurvivor.userUuid : null,
}
})
}
@@ -0,0 +1,238 @@
import {
DecryptedItemInterface,
DecryptedPayload,
FileContent,
FileItem,
FillItemContent,
NoteContent,
PayloadSource,
PayloadTimestampDefaults,
SNNote,
SNTag,
TagContent,
VaultListingInterface,
} from '@standardnotes/models'
import {
SyncServiceInterface,
ItemManagerInterface,
InternalEventBusInterface,
VaultService,
AlertService,
ChangeVaultKeyOptions,
CreateVault,
DeleteVault,
GetVault,
GetVaults,
IsVaultOwner,
MoveItemsToVault,
MutatorClientInterface,
RemoveItemFromVault,
RotateVaultKey,
SendVaultDataChangedMessage,
ValidateVaultPassword,
VaultLockServiceInterface,
} from '../..'
import { AuthorizeVaultDeletion } from './UseCase/AuthorizeVaultDeletion'
import { ContentType } from '@standardnotes/domain-core'
let currentId = 0
const mockUuid = () => {
return `${currentId++}`
}
const createNoteWithContent = (content: Partial<NoteContent>, createdAt?: Date): SNNote => {
return new SNNote(
new DecryptedPayload(
{
uuid: mockUuid(),
content_type: ContentType.TYPES.Note,
content: FillItemContent<NoteContent>(content),
...PayloadTimestampDefaults(),
created_at: createdAt || new Date(),
},
PayloadSource.Constructor,
),
)
}
const createFile = (name: string) => {
return new FileItem(
new DecryptedPayload({
uuid: mockUuid(),
content_type: ContentType.TYPES.File,
content: FillItemContent<FileContent>({
name: name,
}),
...PayloadTimestampDefaults(),
}),
)
}
const createTagWithContent = (content: Partial<TagContent>, key_system_identifier?: string): SNTag => {
return new SNTag(
new DecryptedPayload(
{
uuid: mockUuid(),
content_type: ContentType.TYPES.Tag,
content: FillItemContent<TagContent>(content),
key_system_identifier,
...PayloadTimestampDefaults(),
},
PayloadSource.Constructor,
),
)
}
describe('VaultService', () => {
let sync: SyncServiceInterface
let items: ItemManagerInterface
let mutator: MutatorClientInterface
let vaultLocks: VaultLockServiceInterface
let alerts: AlertService
let _getVault: GetVault
let _getVaults: GetVaults
let _changeVaultKeyOptions: ChangeVaultKeyOptions
let _moveItemsToVault: MoveItemsToVault
let _createVault: CreateVault
let _removeItemFromVault: RemoveItemFromVault
let _deleteVault: DeleteVault
let _rotateVaultKey: RotateVaultKey
let _sendVaultDataChangeMessage: SendVaultDataChangedMessage
let _isVaultOwner: IsVaultOwner
let _validateVaultPassword: ValidateVaultPassword
let _authorizeVaultDeletion: AuthorizeVaultDeletion
let eventBus: InternalEventBusInterface
let service: VaultService
beforeEach(() => {
sync = {} as jest.Mocked<SyncServiceInterface>
items = {} as jest.Mocked<ItemManagerInterface>
mutator = {} as jest.Mocked<MutatorClientInterface>
vaultLocks = {} as jest.Mocked<VaultLockServiceInterface>
vaultLocks.isVaultLocked = jest.fn()
alerts = {} as jest.Mocked<AlertService>
alerts.alertV2 = jest.fn().mockResolvedValue({})
eventBus = {} as jest.Mocked<InternalEventBusInterface>
_getVault = {} as jest.Mocked<GetVault>
_getVaults = {} as jest.Mocked<GetVaults>
_changeVaultKeyOptions = {} as jest.Mocked<ChangeVaultKeyOptions>
_moveItemsToVault = {} as jest.Mocked<MoveItemsToVault>
_moveItemsToVault.execute = jest.fn().mockResolvedValue({})
_createVault = {} as jest.Mocked<CreateVault>
_removeItemFromVault = {} as jest.Mocked<RemoveItemFromVault>
_deleteVault = {} as jest.Mocked<DeleteVault>
_rotateVaultKey = {} as jest.Mocked<RotateVaultKey>
_sendVaultDataChangeMessage = {} as jest.Mocked<SendVaultDataChangedMessage>
_isVaultOwner = {} as jest.Mocked<IsVaultOwner>
_validateVaultPassword = {} as jest.Mocked<ValidateVaultPassword>
_authorizeVaultDeletion = {} as jest.Mocked<AuthorizeVaultDeletion>
service = new VaultService(
sync,
items,
mutator,
vaultLocks,
alerts,
_getVault,
_getVaults,
_changeVaultKeyOptions,
_moveItemsToVault,
_createVault,
_removeItemFromVault,
_deleteVault,
_rotateVaultKey,
_sendVaultDataChangeMessage,
_isVaultOwner,
_validateVaultPassword,
_authorizeVaultDeletion,
eventBus,
)
})
describe('moveItemToVault', () => {
it('should throw error if vault is locked', async () => {
vaultLocks.isVaultLocked = jest.fn().mockReturnValue(true)
const vault = {
uuid: '123',
} as VaultListingInterface
const item = {
uuid: '456',
} as DecryptedItemInterface
await expect(service.moveItemToVault(vault, item)).rejects.toThrow('Attempting to add item to locked vault')
})
describe('moving note/file into vault', () => {
it('should not move note/file if any item linked to it is already in another vault', async () => {
const vault = {
uuid: '123',
systemIdentifier: '456',
} as VaultListingInterface
const note = createNoteWithContent({ title: 'a' })
const file = createFile('b')
const returnValue = [
{
uuid: '789',
key_system_identifier: '234',
},
{
uuid: '012',
},
] as DecryptedItemInterface[]
items.getItemLinkedFiles = jest.fn().mockReturnValue(returnValue)
items.getItemLinkedNotes = jest.fn().mockReturnValue(returnValue)
items.getUnsortedTagsForItem = jest.fn().mockReturnValue(returnValue)
const result = await service.moveItemToVault(vault, note)
expect(result.isFailed()).toBe(true)
const result2 = await service.moveItemToVault(vault, file)
expect(result2.isFailed()).toBe(true)
})
})
describe('moving tag into vault', () => {
it('should not move tag if any deeply nested subtag is already in another vault', async () => {
const vault = {
uuid: '123',
systemIdentifier: '456',
} as VaultListingInterface
const parentTag = createTagWithContent({ title: 'a' })
const subTag = createTagWithContent({ title: 'b' }, '123')
const anotherSubTag = createTagWithContent({ title: 'c' })
items.getDeepTagChildren = jest.fn().mockReturnValue([subTag, anotherSubTag])
const result = await service.moveItemToVault(vault, parentTag)
expect(result.isFailed()).toBe(true)
})
it('should move parent tag and all deeply nested subtags into vault', async () => {
const vault = {
uuid: '123',
systemIdentifier: '456',
} as VaultListingInterface
const parentTag = createTagWithContent({ title: 'a' })
const subTag = createTagWithContent({ title: 'b' })
const anotherSubTag = createTagWithContent({ title: 'c' })
items.getDeepTagChildren = jest.fn().mockReturnValue([subTag, anotherSubTag])
items.findSureItem = jest.fn().mockReturnValue(parentTag.uuid)
const result = await service.moveItemToVault(vault, parentTag)
expect(result.isFailed()).toBe(false)
})
})
})
})
@@ -8,11 +8,13 @@ import {
IconType,
KeySystemIdentifier,
KeySystemRootKeyStorageMode,
SNTag,
SharedVaultListingInterface,
VaultListingInterface,
VaultListingMutator,
isFile,
isNote,
isTag,
} from '@standardnotes/models'
import { VaultServiceInterface } from './VaultServiceInterface'
import { ChangeVaultKeyOptionsDTO } from './UseCase/ChangeVaultKeyOptionsDTO'
@@ -83,22 +85,8 @@ export class VaultService
return this._getVaults.execute().getValue()
}
public getVault(dto: { keySystemIdentifier: KeySystemIdentifier }): VaultListingInterface | undefined {
const result = this._getVault.execute(dto)
if (result.isFailed()) {
return undefined
}
return result.getValue()
}
public getSureVault(dto: { keySystemIdentifier: KeySystemIdentifier }): VaultListingInterface {
const vault = this.getVault(dto)
if (!vault) {
throw new Error('Vault not found')
}
return vault
getVault(dto: { keySystemIdentifier: KeySystemIdentifier }): Result<VaultListingInterface> {
return this._getVault.execute(dto)
}
async createRandomizedVault(dto: {
@@ -146,7 +134,7 @@ export class VaultService
async moveItemToVault(
vault: VaultListingInterface,
item: DecryptedItemInterface,
): Promise<DecryptedItemInterface | undefined> {
): Promise<Result<DecryptedItemInterface>> {
if (this.vaultLocks.isVaultLocked(vault)) {
throw new Error('Attempting to add item to locked vault')
}
@@ -161,19 +149,41 @@ export class VaultService
})
if (areAnyLinkedItemsInOtherVaults) {
const reason =
'This item is linked to other items that are not in the same vault. Please move those items to this vault first.'
this.alerts
.alertV2({
title: 'Cannot move item to vault',
text: 'This item is linked to other items that are not in the same vault. Please move those items to this vault first.',
text: reason,
})
.catch(console.error)
return undefined
return Result.fail(reason)
}
}
await this._moveItemsToVault.execute({ vault, items: [item] })
let moveableSubtags: SNTag[] = []
if (isTag(item)) {
const deepSubtags = this.items.getDeepTagChildren(item)
const anySubtagIsInOtherVault = deepSubtags.some((subtag) => {
return subtag.key_system_identifier && subtag.key_system_identifier !== vault.systemIdentifier
})
if (anySubtagIsInOtherVault) {
const reason =
'One or more subtags are in other vaults. Please remove those subtags from the vaults or move them to this vault first.'
this.alerts
.alertV2({
title: 'Cannot move item to vault',
text: reason,
})
.catch(console.error)
return Result.fail(reason)
}
moveableSubtags = deepSubtags
}
return this.items.findSureItem(item.uuid)
await this._moveItemsToVault.execute({ vault, items: [item, ...moveableSubtags] })
return Result.ok(this.items.findSureItem(item.uuid))
}
async removeItemFromVault(item: DecryptedItemInterface): Promise<DecryptedItemInterface> {
@@ -186,6 +196,13 @@ export class VaultService
throw new Error('Attempting to remove item from locked vault')
}
if (isTag(item)) {
const deepSubtags = this.items.getDeepTagChildren(item)
for (const subtag of deepSubtags) {
await this._removeItemFromVault.execute({ item: subtag })
}
}
await this._removeItemFromVault.execute({ item })
return this.items.findSureItem(item.uuid)
}
@@ -248,12 +265,12 @@ export class VaultService
}
getItemVault(item: DecryptedItemInterface): VaultListingInterface | undefined {
const latestItem = this.items.findItem(item.uuid)
if (this.items.isTemplateItem(item)) {
return undefined
}
const latestItem = this.items.findItem(item.uuid)
if (!latestItem) {
throw new Error('Cannot find latest version of item to get vault for')
}
@@ -262,7 +279,12 @@ export class VaultService
return undefined
}
return this.getVault({ keySystemIdentifier: latestItem.key_system_identifier })
const vault = this.getVault({ keySystemIdentifier: latestItem.key_system_identifier })
if (vault.isFailed()) {
throw new Error('Cannot find vault for item')
}
return vault.getValue()
}
async changeVaultKeyOptions(dto: ChangeVaultKeyOptionsDTO): Promise<Result<void>> {
@@ -28,14 +28,11 @@ export interface VaultServiceInterface
}): Promise<VaultListingInterface>
getVaults(): VaultListingInterface[]
getVault(dto: { keySystemIdentifier: KeySystemIdentifier }): VaultListingInterface | undefined
getVault(dto: { keySystemIdentifier: KeySystemIdentifier }): Result<VaultListingInterface>
authorizeVaultDeletion(vault: VaultListingInterface): Promise<Result<boolean>>
deleteVault(vault: VaultListingInterface): Promise<boolean>
moveItemToVault(
vault: VaultListingInterface,
item: DecryptedItemInterface,
): Promise<DecryptedItemInterface | undefined>
moveItemToVault(vault: VaultListingInterface, item: DecryptedItemInterface): Promise<Result<DecryptedItemInterface>>
removeItemFromVault(item: DecryptedItemInterface): Promise<DecryptedItemInterface>
isItemInVault(item: DecryptedItemInterface): boolean
getItemVault(item: DecryptedItemInterface): VaultListingInterface | undefined
@@ -124,6 +124,7 @@ export class InviteToVault implements UseCaseInterface<SharedVaultInviteServerHa
description: params.sharedVault.description,
iconString: params.sharedVault.iconString,
fileBytesUsed: params.sharedVault.sharing.fileBytesUsed,
designatedSurvivor: params.sharedVault.sharing.designatedSurvivor,
},
},
},
@@ -6,7 +6,7 @@ import {
isClientDisplayableError,
isErrorResponse,
} from '@standardnotes/responses'
import { ContentType, Result } from '@standardnotes/domain-core'
import { ContentType, NotificationType, Result } from '@standardnotes/domain-core'
import { SharedVaultInvitesServer } from '@standardnotes/api'
import {
AsymmetricMessageSharedVaultInvite,
@@ -40,6 +40,7 @@ import { DecryptErroredPayloads } from '../Encryption/UseCase/DecryptErroredPayl
import { StatusServiceInterface } from '../Status/StatusServiceInterface'
import { ApplicationEvent } from '../Event/ApplicationEvent'
import { WebSocketsServiceEvent } from '../Api/WebSocketsServiceEvent'
import { NotificationServiceEvent, NotificationServiceEventPayload } from '../UserEvent/NotificationServiceEvent'
export class VaultInviteService
extends AbstractService<VaultInviteServiceEvent>
@@ -124,12 +125,25 @@ export class VaultInviteService
}
void this.downloadInboundInvites()
break
case NotificationServiceEvent.NotificationReceived:
await this.handleNotification(event.payload as NotificationServiceEventPayload)
break
case WebSocketsServiceEvent.UserInvitedToSharedVault:
await this.processInboundInvites([(event as UserInvitedToSharedVaultEvent).payload.invite])
break
}
}
private async handleNotification(event: NotificationServiceEventPayload): Promise<void> {
switch (event.eventPayload.props.type.value) {
case NotificationType.TYPES.SharedVaultInviteCanceled: {
this.removePendingInvite(event.eventPayload.props.primaryIdentifier.value)
void this.notifyEvent(VaultInviteServiceEvent.InvitesReloaded)
break
}
}
}
public getCachedPendingInviteRecords(): InviteRecord[] {
return Object.values(this.pendingInvites)
}
@@ -179,7 +193,8 @@ export class VaultInviteService
this.removePendingInvite(pendingInvite.invite.uuid)
void this.sync.sync()
this.sync.sync().catch(console.error)
this.vaultUsers.invalidateVaultUsersCache(pendingInvite.invite.shared_vault_uuid).catch(console.error)
await this._decryptErroredPayloads.execute()
@@ -0,0 +1,66 @@
import { SharedVaultUsersServerInterface } from '@standardnotes/api'
import { DesignateSurvivor } from './DesignateSurvivor'
describe('DesignateSurvivor', () => {
let sharedVaultUserServer: SharedVaultUsersServerInterface
const createUseCase = () => new DesignateSurvivor(
sharedVaultUserServer,
)
beforeEach(() => {
sharedVaultUserServer = {} as jest.Mocked<SharedVaultUsersServerInterface>
sharedVaultUserServer.designateSurvivor = jest.fn().mockReturnValue({
status: 200,
})
})
it('should mark designated survivor', async () => {
const useCase = createUseCase()
const result = await useCase.execute({
sharedVaultUuid: '00000000-0000-0000-0000-000000000000',
sharedVaultMemberUuid: '00000000-0000-0000-0000-000000000000',
})
expect(result.isFailed()).toBe(false)
})
it('should fail if shared vault uuid is invalid', async () => {
const useCase = createUseCase()
const result = await useCase.execute({
sharedVaultUuid: 'invalid',
sharedVaultMemberUuid: '00000000-0000-0000-0000-000000000000',
})
expect(result.isFailed()).toBe(true)
})
it('should fail if shared vault member uuid is invalid', async () => {
const useCase = createUseCase()
const result = await useCase.execute({
sharedVaultUuid: '00000000-0000-0000-0000-000000000000',
sharedVaultMemberUuid: 'invalid',
})
expect(result.isFailed()).toBe(true)
})
it('should fail if shared vault user server fails', async () => {
sharedVaultUserServer.designateSurvivor = jest.fn().mockReturnValue({
status: 500,
})
const useCase = createUseCase()
const result = await useCase.execute({
sharedVaultUuid: '00000000-0000-0000-0000-000000000000',
sharedVaultMemberUuid: '00000000-0000-0000-0000-000000000000',
})
expect(result.isFailed()).toBe(true)
})
})
@@ -0,0 +1,34 @@
import { Result, UseCaseInterface, Uuid } from '@standardnotes/domain-core'
import { SharedVaultUsersServerInterface } from '@standardnotes/api'
import { HttpStatusCode } from '@standardnotes/responses'
import { DesignateSurvivorDTO } from './DesignateSurvivorDTO'
export class DesignateSurvivor implements UseCaseInterface<void> {
constructor(private sharedVaultUserServer: SharedVaultUsersServerInterface) {}
async execute(dto: DesignateSurvivorDTO): Promise<Result<void>> {
const sharedVaultUuidOrError = Uuid.create(dto.sharedVaultUuid)
if (sharedVaultUuidOrError.isFailed()) {
return Result.fail(sharedVaultUuidOrError.getError())
}
const sharedVaultUuid = sharedVaultUuidOrError.getValue()
const sharedVaultMemberUuidOrError = Uuid.create(dto.sharedVaultMemberUuid)
if (sharedVaultMemberUuidOrError.isFailed()) {
return Result.fail(sharedVaultMemberUuidOrError.getError())
}
const sharedVaultMemberUuid = sharedVaultMemberUuidOrError.getValue()
const response = await this.sharedVaultUserServer.designateSurvivor({
sharedVaultUuid,
sharedVaultMemberUuid,
})
if (response.status !== HttpStatusCode.Success) {
return Result.fail('Failed to mark designated survivor on the server')
}
return Result.ok()
}
}
@@ -0,0 +1,4 @@
export interface DesignateSurvivorDTO {
sharedVaultUuid: string
sharedVaultMemberUuid: string
}
@@ -12,16 +12,11 @@ import { AbstractService } from './../Service/AbstractService'
import { VaultUserServiceEvent } from './VaultUserServiceEvent'
import { Result } from '@standardnotes/domain-core'
import { IsVaultOwner } from './UseCase/IsVaultOwner'
import { InternalEventInterface } from '../Internal/InternalEventInterface'
import { InternalEventHandlerInterface } from '../Internal/InternalEventHandlerInterface'
import { ApplicationEvent } from '../Event/ApplicationEvent'
import { IsReadonlyVaultMember } from './UseCase/IsReadonlyVaultMember'
import { IsVaultAdmin } from './UseCase/IsVaultAdmin'
import { DesignateSurvivor } from './UseCase/DesignateSurvivor/DesignateSurvivor'
export class VaultUserService
extends AbstractService<VaultUserServiceEvent>
implements VaultUserServiceInterface, InternalEventHandlerInterface
{
export class VaultUserService extends AbstractService<VaultUserServiceEvent> implements VaultUserServiceInterface {
constructor(
private vaults: VaultServiceInterface,
private vaultLocks: VaultLockServiceInterface,
@@ -32,6 +27,7 @@ export class VaultUserService
private _isReadonlyVaultMember: IsReadonlyVaultMember,
private _getVault: GetVault,
private _leaveVault: LeaveVault,
private designateSurvivorUseCase: DesignateSurvivor,
eventBus: InternalEventBusInterface,
) {
super(eventBus)
@@ -47,20 +43,41 @@ export class VaultUserService
;(this._leaveVault as unknown) = undefined
}
async handleEvent(event: InternalEventInterface): Promise<void> {
if (event.type === ApplicationEvent.CompletedFullSync) {
this.vaults.getVaults().forEach((vault) => {
async designateSurvivor(sharedVault: SharedVaultListingInterface, userUuid: string): Promise<Result<void>> {
const result = await this.designateSurvivorUseCase.execute({
sharedVaultMemberUuid: userUuid,
sharedVaultUuid: sharedVault.sharing.sharedVaultUuid,
})
if (result.isFailed()) {
return Result.fail(`Could not designate survivor: ${result.getError()}`)
}
return Result.ok()
}
async invalidateVaultUsersCache(sharedVaultUuid?: string) {
if (sharedVaultUuid) {
await this._getVaultUsers.execute({
sharedVaultUuid: sharedVaultUuid,
readFromCache: false,
})
void this.notifyEvent(VaultUserServiceEvent.InvalidatedUserCacheForVault, sharedVaultUuid)
return
}
await Promise.all(
this.vaults.getVaults().map(async (vault) => {
if (!vault.isSharedVaultListing()) {
return
}
this._getVaultUsers
.execute({
sharedVaultUuid: vault.sharing.sharedVaultUuid,
readFromCache: false,
})
.catch(console.error)
})
}
await this._getVaultUsers.execute({
sharedVaultUuid: vault.sharing.sharedVaultUuid,
readFromCache: false,
})
void this.notifyEvent(VaultUserServiceEvent.InvalidatedUserCacheForVault, vault.sharing.sharedVaultUuid)
}),
)
void this.notifyEvent(VaultUserServiceEvent.InvalidatedAllUserCache)
}
public async getSharedVaultUsersFromServer(
@@ -1,3 +1,5 @@
export enum VaultUserServiceEvent {
UsersChanged = 'VaultUserServiceEvent.UsersChanged',
InvalidatedAllUserCache = 'VaultUserServiceEvent.InvalidatedUserCache',
InvalidatedUserCacheForVault = 'VaultUserServiceEvent.InvalidatedUserCacheForVault',
}
@@ -12,7 +12,9 @@ export interface VaultUserServiceInterface extends ApplicationServiceInterface<V
isCurrentUserSharedVaultAdmin(sharedVault: SharedVaultListingInterface): boolean
isCurrentUserReadonlyVaultMember(vault: VaultListingInterface): boolean
removeUserFromSharedVault(sharedVault: SharedVaultListingInterface, userUuid: string): Promise<Result<void>>
designateSurvivor(sharedVault: SharedVaultListingInterface, userUuid: string): Promise<Result<void>>
leaveSharedVault(sharedVault: SharedVaultListingInterface): Promise<ClientDisplayableError | void>
isVaultUserOwner(user: SharedVaultUserServerHash): boolean
getFormattedMemberPermission(permission: string): string
invalidateVaultUsersCache(sharedVaultUuid?: string): Promise<void>
}
+5 -1
View File
@@ -203,6 +203,7 @@ export * from './User/UserServiceInterface'
export * from './User/UserServiceInterface'
export * from './UserEvent/NotificationService'
export * from './UserEvent/NotificationServiceEvent'
export * from './Vault/UseCase/AuthorizeVaultDeletion'
export * from './Vault/UseCase/ChangeVaultKeyOptions'
export * from './Vault/UseCase/ChangeVaultKeyOptionsDTO'
export * from './Vault/UseCase/ChangeVaultStorageMode'
@@ -231,9 +232,12 @@ export * from './VaultLock/UseCase/ValidateVaultPassword'
export * from './VaultLock/VaultLockService'
export * from './VaultLock/VaultLockServiceEvent'
export * from './VaultLock/VaultLockServiceInterface'
export * from './VaultUser/UseCase/GetVaultContacts'
export * from './VaultUser/UseCase/DesignateSurvivor/DesignateSurvivor'
export * from './VaultUser/UseCase/DesignateSurvivor/DesignateSurvivorDTO'
export * from './VaultUser/UseCase/GetVaultContacts'
export * from './VaultUser/UseCase/GetVaultUsers'
export * from './VaultUser/UseCase/IsReadonlyVaultMember'
export * from './VaultUser/UseCase/IsVaultAdmin'
export * from './VaultUser/UseCase/IsVaultOwner'
export * from './VaultUser/UseCase/LeaveSharedVault'
export * from './VaultUser/UseCase/RemoveSharedVaultMember'
+64
View File
@@ -3,6 +3,70 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [2.202.92](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-28)
**Note:** Version bump only for package @standardnotes/snjs
## [2.202.91](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-26)
**Note:** Version bump only for package @standardnotes/snjs
## [2.202.90](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-26)
**Note:** Version bump only for package @standardnotes/snjs
## [2.202.89](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-26)
**Note:** Version bump only for package @standardnotes/snjs
## [2.202.88](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-25)
**Note:** Version bump only for package @standardnotes/snjs
## [2.202.87](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-22)
**Note:** Version bump only for package @standardnotes/snjs
## [2.202.86](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-22)
**Note:** Version bump only for package @standardnotes/snjs
## [2.202.85](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-22)
**Note:** Version bump only for package @standardnotes/snjs
## [2.202.84](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-22)
**Note:** Version bump only for package @standardnotes/snjs
## [2.202.83](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-21)
**Note:** Version bump only for package @standardnotes/snjs
## [2.202.82](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-20)
**Note:** Version bump only for package @standardnotes/snjs
## [2.202.81](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-20)
**Note:** Version bump only for package @standardnotes/snjs
## [2.202.80](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-20)
**Note:** Version bump only for package @standardnotes/snjs
## [2.202.79](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-20)
**Note:** Version bump only for package @standardnotes/snjs
## [2.202.78](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-19)
**Note:** Version bump only for package @standardnotes/snjs
## [2.202.77](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-19)
**Note:** Version bump only for package @standardnotes/snjs
## [2.202.76](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-18)
**Note:** Version bump only for package @standardnotes/snjs
@@ -469,6 +469,7 @@ export class SNApplication implements ApplicationInterface, AppGroupManagedAppli
source: SyncSource.External,
sourceDescription: 'Application Launch',
})
this.vaultUsers.invalidateVaultUsersCache().catch(console.error)
})
.catch((error) => {
void this.notifyEvent(ApplicationEvent.LocalDatabaseReadError, error)
@@ -141,6 +141,10 @@ import {
CreateEncryptedBackupFile,
SyncLocalVaultsWithRemoteSharedVaults,
WebSocketsService,
AuthorizeVaultDeletion,
IsVaultAdmin,
IsReadonlyVaultMember,
DesignateSurvivor,
} from '@standardnotes/services'
import { ItemManager } from '../../Services/Items/ItemManager'
import { PayloadManager } from '../../Services/Payloads/PayloadManager'
@@ -158,6 +162,7 @@ import {
SharedVaultInvitesServer,
SharedVaultServer,
SharedVaultUsersServer,
SharedVaultUsersServerInterface,
SubscriptionApiService,
SubscriptionServer,
UserApiService,
@@ -171,9 +176,6 @@ import { Logger, isNotUndefined, isDeinitable, LoggerInterface } from '@standard
import { EncryptionOperators } from '@standardnotes/encryption'
import { AsymmetricMessagePayload, AsymmetricMessageSharedVaultInvite } from '@standardnotes/models'
import { PureCryptoInterface } from '@standardnotes/sncrypto-common'
import { AuthorizeVaultDeletion } from '@standardnotes/services/src/Domain/Vault/UseCase/AuthorizeVaultDeletion'
import { IsVaultAdmin } from '@standardnotes/services/src/Domain/VaultUser/UseCase/IsVaultAdmin'
import { IsReadonlyVaultMember } from '@standardnotes/services/src/Domain/VaultUser/UseCase/IsReadonlyVaultMember'
export class Dependencies {
private factory = new Map<symbol, () => unknown>()
@@ -683,6 +685,10 @@ export class Dependencies {
return new RemoveVaultMember(this.get<SharedVaultUsersServer>(TYPES.SharedVaultUsersServer))
})
this.factory.set(TYPES.DesignateSurvivor, () => {
return new DesignateSurvivor(this.get<SharedVaultUsersServerInterface>(TYPES.SharedVaultUsersServer))
})
this.factory.set(TYPES.GetVaultUsers, () => {
return new GetVaultUsers(
this.get<SharedVaultUsersServer>(TYPES.SharedVaultUsersServer),
@@ -861,6 +867,7 @@ export class Dependencies {
this.get<IsReadonlyVaultMember>(TYPES.IsReadonlyVaultMember),
this.get<GetVault>(TYPES.GetVault),
this.get<LeaveVault>(TYPES.LeaveVault),
this.get<DesignateSurvivor>(TYPES.DesignateSurvivor),
this.get<InternalEventBus>(TYPES.InternalEventBus),
)
})
@@ -916,6 +923,7 @@ export class Dependencies {
return new SharedVaultService(
this.get<ItemManager>(TYPES.ItemManager),
this.get<SessionManager>(TYPES.SessionManager),
this.get<VaultUserService>(TYPES.VaultUserService),
this.get<SyncLocalVaultsWithRemoteSharedVaults>(TYPES.SyncLocalVaultsWithRemoteSharedVaults),
this.get<GetVault>(TYPES.GetVault),
this.get<GetOwnedSharedVaults>(TYPES.GetOwnedSharedVaults),
@@ -31,6 +31,7 @@ export function RegisterApplicationServicesEvents(container: Dependencies, event
events.addEventHandler(container.get(TYPES.SessionManager), ApiServiceEvent.SessionRefreshed)
events.addEventHandler(container.get(TYPES.SessionManager), ApplicationEvent.ApplicationStageChanged)
events.addEventHandler(container.get(TYPES.SharedVaultService), NotificationServiceEvent.NotificationReceived)
events.addEventHandler(container.get(TYPES.VaultInviteService), NotificationServiceEvent.NotificationReceived)
events.addEventHandler(container.get(TYPES.SharedVaultService), SessionEvent.UserKeyPairChanged)
events.addEventHandler(container.get(TYPES.SharedVaultService), SyncEvent.ReceivedRemoteSharedVaults)
events.addEventHandler(container.get(TYPES.SubscriptionManager), ApplicationEvent.ApplicationStageChanged)
@@ -43,7 +44,6 @@ export function RegisterApplicationServicesEvents(container: Dependencies, event
events.addEventHandler(container.get(TYPES.VaultInviteService), ApplicationEvent.Launched)
events.addEventHandler(container.get(TYPES.VaultInviteService), SyncEvent.ReceivedSharedVaultInvites)
events.addEventHandler(container.get(TYPES.VaultInviteService), WebSocketsServiceEvent.UserInvitedToSharedVault)
events.addEventHandler(container.get(TYPES.VaultUserService), ApplicationEvent.CompletedFullSync)
if (container.get(TYPES.FilesBackupService)) {
events.addEventHandler(container.get(TYPES.FilesBackupService), ApplicationEvent.ApplicationStageChanged)
@@ -125,6 +125,7 @@ export const TYPES = {
ConvertToSharedVault: Symbol.for('ConvertToSharedVault'),
DeleteSharedVault: Symbol.for('DeleteSharedVault'),
RemoveVaultMember: Symbol.for('RemoveVaultMember'),
DesignateSurvivor: Symbol.for('DesignateSurvivor'),
GetVaultUsers: Symbol.for('GetSharedVaultUsers'),
ResendAllMessages: Symbol.for('ResendAllMessages'),
ReuploadAllInvites: Symbol.for('ReuploadAllInvites'),
@@ -702,6 +702,18 @@ export class ItemManager extends Services.AbstractService implements Services.It
return tags.filter((tag) => tag.parentId === itemToLookupUuidFor.uuid)
}
public getDeepTagChildren(itemToLookupUuidFor: Models.SNTag): Models.SNTag[] {
const allChildren: Models.SNTag[] = []
const children = this.getTagChildren(itemToLookupUuidFor)
for (const child of children) {
allChildren.push(child)
allChildren.push(...this.getDeepTagChildren(child))
}
return allChildren
}
public isTagAncestor(tagToLookUpUuidFor: Models.SNTag, childToLookUpUuidFor: Models.SNTag): boolean {
const tag = this.findItem<Models.SNTag>(childToLookUpUuidFor.uuid)
if (!tag) {
@@ -875,7 +887,13 @@ export class ItemManager extends Services.AbstractService implements Services.It
}
public numberOfNotesWithConflicts(): number {
return this.findItems(this.collection.uuidsOfItemsWithConflicts()).filter(Models.isNote).length
const uuids = this.collection.uuidsOfItemsWithConflicts()
const items = this.navigationDisplayController.hasExclusiveVaultOptions()
? this.navigationDisplayController
.items()
.filter((item) => Models.isNote(item) && this.collection.uuidsOfItemsWithConflicts().includes(item.uuid))
: this.findItems(uuids).filter(Models.isNote)
return items.length
}
getItemLinkedFiles(item: Models.DecryptedItemInterface): Models.FileItem[] {
+63 -59
View File
@@ -1,59 +1,63 @@
export const BaseTests = [
'memory.test.js',
'protocol.test.js',
'utils.test.js',
'000.test.js',
'001.test.js',
'002.test.js',
'003.test.js',
'004.test.js',
'username.test.js',
'app-group.test.js',
'application.test.js',
'payload.test.js',
'payload_encryption.test.js',
'item.test.js',
'item_manager.test.js',
'features.test.js',
'settings.test.js',
'mfa_service.test.js',
'mutator.test.js',
'mutator_service.test.js',
'payload_manager.test.js',
'collections.test.js',
'note_display_criteria.test.js',
'keys.test.js',
'key_params.test.js',
'key_recovery_service.test.js',
'backups.test.js',
'upgrading.test.js',
'model_tests/importing.test.js',
'model_tests/appmodels.test.js',
'model_tests/items.test.js',
'model_tests/mapping.test.js',
'model_tests/notes_smart_tags.test.js',
'model_tests/notes_tags.test.js',
'model_tests/notes_tags_folders.test.js',
'model_tests/performance.test.js',
'sync_tests/offline.test.js',
'sync_tests/notes_tags.test.js',
'sync_tests/online.test.js',
'sync_tests/conflicting.test.js',
'sync_tests/integrity.test.js',
'auth-fringe-cases.test.js',
'auth.test.js',
'device_auth.test.js',
'storage.test.js',
'protection.test.js',
'singletons.test.js',
'migrations/migration.test.js',
'migrations/tags-to-folders.test.js',
'history.test.js',
'actions.test.js',
'preferences.test.js',
'files.test.js',
'session.test.js',
'session-invalidation.test.js',
'subscriptions.test.js',
'recovery.test.js',
]
export const BaseTests = {
enabled: true,
exclusive: false,
files: [
'memory.test.js',
'protocol.test.js',
'utils.test.js',
'000.test.js',
'001.test.js',
'002.test.js',
'003.test.js',
'004.test.js',
'username.test.js',
'app-group.test.js',
'application.test.js',
'payload.test.js',
'payload_encryption.test.js',
'item.test.js',
'item_manager.test.js',
'features.test.js',
'settings.test.js',
'mfa_service.test.js',
'mutator.test.js',
'mutator_service.test.js',
'payload_manager.test.js',
'collections.test.js',
'note_display_criteria.test.js',
'keys.test.js',
'key_params.test.js',
'key_recovery_service.test.js',
'backups.test.js',
'upgrading.test.js',
'model_tests/importing.test.js',
'model_tests/appmodels.test.js',
'model_tests/items.test.js',
'model_tests/mapping.test.js',
'model_tests/notes_smart_tags.test.js',
'model_tests/notes_tags.test.js',
'model_tests/notes_tags_folders.test.js',
'model_tests/performance.test.js',
'sync_tests/offline.test.js',
'sync_tests/notes_tags.test.js',
'sync_tests/online.test.js',
'sync_tests/conflicting.test.js',
'sync_tests/integrity.test.js',
'auth-fringe-cases.test.js',
'auth.test.js',
'device_auth.test.js',
'storage.test.js',
'protection.test.js',
'singletons.test.js',
'migrations/migration.test.js',
'migrations/tags-to-folders.test.js',
'history.test.js',
'actions.test.js',
'preferences.test.js',
'files.test.js',
'session.test.js',
'session-invalidation.test.js',
'subscriptions.test.js',
'recovery.test.js',
]
}
@@ -1,5 +1,5 @@
export const VaultTests = {
enabled: false,
enabled: true,
exclusive: false,
files: [
'vaults/vaults.test.js',
@@ -23,5 +23,6 @@ export const VaultTests = {
'vaults/files.test.js',
'vaults/limits.test.js',
'vaults/quota.test.js',
'vaults/surviving.test.js',
],
}
+126 -89
View File
@@ -4,26 +4,20 @@ import { createNoteParams } from './lib/Items.js'
chai.use(chaiAsPromised)
const expect = chai.expect
describe.skip('history manager', () => {
describe('history manager', () => {
const largeCharacterChange = 25
let application, history, email, password
const syncOptions = {
checkIntegrity: true,
awaitAll: true,
}
beforeEach(function () {
localStorage.clear()
})
afterEach(function () {
localStorage.clear()
})
describe('session', function () {
let application, history
beforeEach(async function () {
localStorage.clear()
application = await Factory.createInitAppWithFakeCrypto()
history = application.dependencies.get(TYPES.HistoryManager)
@@ -33,6 +27,7 @@ describe.skip('history manager', () => {
afterEach(async function () {
await Factory.safeDeinit(application)
localStorage.clear()
})
async function setTextAndSync(application, item, text) {
@@ -234,96 +229,102 @@ describe.skip('history manager', () => {
describe('remote', function () {
this.timeout(Factory.TwentySecondTimeout)
let context
beforeEach(async function () {
localStorage.clear()
application = await Factory.createInitAppWithFakeCrypto()
history = application.dependencies.get(TYPES.HistoryManager)
email = UuidGenerator.GenerateUuid()
password = UuidGenerator.GenerateUuid()
await Factory.registerUserToApplication({
application: application,
email: email,
password: password,
})
context = await Factory.createVaultsContextWithRealCrypto()
await context.launch()
await context.register()
})
afterEach(async function () {
await Factory.safeDeinit(application)
await context.deinit()
localStorage.clear()
sinon.restore()
context = undefined
})
it('response from server should be failed if not signed in', async function () {
await application.user.signOut()
application = await Factory.createInitAppWithFakeCrypto()
history = application.dependencies.get(TYPES.HistoryManager)
const note = await context.createSyncedNote('test note')
const item = await Factory.createSyncedNote(application)
await application.sync.sync(syncOptions)
const itemHistoryOrError = await application.listRevisions.execute({ itemUuid: item.uuid })
await context.signout()
const itemHistoryOrError = await context.application.listRevisions.execute({ itemUuid: note.uuid })
expect(itemHistoryOrError.isFailed()).to.equal(true)
})
it('should save initial revisions on server', async () => {
const item = await Factory.createSyncedNote(application)
expect(item).to.be.ok
const note = await context.createSyncedNote('test note')
expect(note).to.be.ok
await Factory.sleep(Factory.ServerRevisionCreationDelay)
const itemHistoryOrError = await application.listRevisions.execute({ itemUuid: item.uuid })
const itemHistoryOrError = await context.application.listRevisions.execute({ itemUuid: note.uuid })
expect(itemHistoryOrError.isFailed()).to.equal(false)
const itemHistory = itemHistoryOrError.getValue()
let itemHistory = itemHistoryOrError.getValue()
if (itemHistory.length === 0) {
await Factory.sleep(Factory.ServerRevisionCreationDelay, 'No revisions found on the server. This is likely a delay issue. Retrying...')
const itemHistoryOrError = await context.application.listRevisions.execute({ itemUuid: note.uuid })
expect(itemHistoryOrError.isFailed()).to.equal(false)
itemHistory = itemHistoryOrError.getValue()
}
expect(itemHistory.length >= 1).to.be.true
})
it('should not create new revisions within the revision frequency window', async () => {
const item = await Factory.createSyncedNote(application)
const note = await context.createSyncedNote('test note')
await application.changeAndSaveItem.execute(
item,
(mutator) => {
mutator.title = Math.random()
},
undefined,
undefined,
syncOptions,
)
await context.changeNoteTitleAndSync(note, 'new title 1')
await Factory.sleep(Factory.ServerRevisionCreationDelay)
const itemHistoryOrError = await application.listRevisions.execute({ itemUuid: item.uuid })
const itemHistoryOrError = await context.application.listRevisions.execute({ itemUuid: note.uuid })
expect(itemHistoryOrError.isFailed()).to.equal(false)
const itemHistory = itemHistoryOrError.getValue()
let itemHistory = itemHistoryOrError.getValue()
if (itemHistory.length === 0) {
await Factory.sleep(Factory.ServerRevisionCreationDelay, 'No revisions found on the server. This is likely a delay issue. Retrying...')
const itemHistoryOrError = await context.application.listRevisions.execute({ itemUuid: note.uuid })
expect(itemHistoryOrError.isFailed()).to.equal(false)
itemHistory = itemHistoryOrError.getValue()
}
expect(itemHistory.length >= 1).to.be.true
})
it('should create new revisions outside the revision frequency window', async function () {
let item = await Factory.createSyncedNote(application)
const note = await context.createSyncedNote('test note')
await Factory.sleep(Factory.ServerRevisionFrequency)
/** Sync with different contents, should create new entry */
const newTitleAfterFirstChange = `The title should be: ${Math.random()}`
await application.changeAndSaveItem.execute(
item,
(mutator) => {
mutator.title = newTitleAfterFirstChange
},
undefined,
undefined,
syncOptions,
)
await context.changeNoteTitleAndSync(note, `The title should be: ${Math.random()}`)
await Factory.sleep(Factory.ServerRevisionCreationDelay)
const itemHistoryOrError = await application.listRevisions.execute({ itemUuid: item.uuid })
const itemHistory = itemHistoryOrError.getValue()
const itemHistoryOrError = await context.application.listRevisions.execute({ itemUuid: note.uuid })
expect(itemHistoryOrError.isFailed()).to.equal(false)
let itemHistory = itemHistoryOrError.getValue()
if (itemHistory.length !== 2) {
await Factory.sleep(Factory.ServerRevisionCreationDelay, 'Not enough revisions found on the server. This is likely a delay issue. Retrying...')
const itemHistoryOrError = await context.application.listRevisions.execute({ itemUuid: note.uuid })
expect(itemHistoryOrError.isFailed()).to.equal(false)
itemHistory = itemHistoryOrError.getValue()
}
expect(itemHistory.length).to.equal(2)
const oldestEntry = lastElement(itemHistory)
let revisionFromServerOrError = await application.getRevision.execute({
itemUuid: item.uuid,
let revisionFromServerOrError = await context.application.getRevision.execute({
itemUuid: note.uuid,
revisionUuid: oldestEntry.uuid,
})
const revisionFromServer = revisionFromServerOrError.getValue()
@@ -331,25 +332,36 @@ describe.skip('history manager', () => {
let payloadFromServer = revisionFromServer.payload
expect(payloadFromServer.errorDecrypting).to.be.undefined
expect(payloadFromServer.uuid).to.eq(item.payload.uuid)
expect(payloadFromServer.content).to.eql(item.payload.content)
expect(payloadFromServer.uuid).to.eq(note.payload.uuid)
expect(payloadFromServer.content).to.eql(note.payload.content)
item = application.items.findItem(item.uuid)
const item = context.application.items.findItem(note.uuid)
expect(payloadFromServer.content).to.not.eql(item.payload.content)
})
it('duplicate revisions should not have the originals uuid', async function () {
const note = await Factory.createSyncedNote(application)
await Factory.markDirtyAndSyncItem(application, note)
const dupe = await application.mutator.duplicateItem(note, true)
await Factory.markDirtyAndSyncItem(application, dupe)
const note = await context.createSyncedNote('test note')
await Factory.markDirtyAndSyncItem(context.application, note)
const dupe = await context.application.mutator.duplicateItem(note, true)
await Factory.markDirtyAndSyncItem(context.application, dupe)
await Factory.sleep(Factory.ServerRevisionCreationDelay)
const dupeHistoryOrError = await application.listRevisions.execute({ itemUuid: dupe.uuid })
const dupeHistory = dupeHistoryOrError.getValue()
const dupeHistoryOrError = await context.application.listRevisions.execute({ itemUuid: dupe.uuid })
expect(dupeHistoryOrError.isFailed()).to.equal(false)
let dupeHistory = dupeHistoryOrError.getValue()
if (dupeHistory.length === 0) {
await Factory.sleep(Factory.ServerRevisionCreationDelay, 'No revisions found on the server. This is likely a delay issue. Retrying...')
const dupeRevisionOrError = await application.getRevision.execute({
const dupeHistoryOrError = await context.application.listRevisions.execute({ itemUuid: dupe.uuid })
expect(dupeHistoryOrError.isFailed()).to.equal(false)
dupeHistory = dupeHistoryOrError.getValue()
}
expect(dupeHistory.length >= 1).to.be.true
const dupeRevisionOrError = await context.application.getRevision.execute({
itemUuid: dupe.uuid,
revisionUuid: dupeHistory[0].uuid,
})
@@ -358,54 +370,79 @@ describe.skip('history manager', () => {
})
it('revisions count matches original for duplicated items', async function () {
const note = await Factory.createSyncedNote(application)
const note = await context.createSyncedNote('test note')
await Factory.sleep(Factory.ServerRevisionFrequency)
await Factory.markDirtyAndSyncItem(application, note)
await Factory.markDirtyAndSyncItem(context.application, note)
await Factory.sleep(Factory.ServerRevisionFrequency)
await Factory.markDirtyAndSyncItem(application, note)
await Factory.markDirtyAndSyncItem(context.application, note)
await Factory.sleep(Factory.ServerRevisionFrequency)
await Factory.markDirtyAndSyncItem(application, note)
await Factory.markDirtyAndSyncItem(context.application, note)
const dupe = await application.mutator.duplicateItem(note, true)
await Factory.markDirtyAndSyncItem(application, dupe)
const dupe = await context.application.mutator.duplicateItem(note, true)
await Factory.markDirtyAndSyncItem(context.application, dupe)
await Factory.sleep(Factory.ServerRevisionCreationDelay)
const expectedRevisions = 4
const noteHistoryOrError = await application.listRevisions.execute({ itemUuid: note.uuid })
const noteHistory = noteHistoryOrError.getValue()
const noteHistoryOrError = await context.application.listRevisions.execute({ itemUuid: note.uuid })
expect(noteHistoryOrError.isFailed()).to.equal(false)
let noteHistory = noteHistoryOrError.getValue()
if (noteHistory.length !== expectedRevisions) {
await Factory.sleep(Factory.ServerRevisionCreationDelay, 'No revisions found on the server. This is likely a delay issue. Retrying...')
const dupeHistoryOrError = await application.listRevisions.execute({ itemUuid: dupe.uuid })
const dupeHistory = dupeHistoryOrError.getValue()
const noteHistoryOrError = await context.application.listRevisions.execute({ itemUuid: note.uuid })
expect(noteHistoryOrError.isFailed()).to.equal(false)
noteHistory = noteHistoryOrError.getValue()
}
expect(noteHistory.length).to.equal(expectedRevisions)
const dupeHistoryOrError = await context.application.listRevisions.execute({ itemUuid: dupe.uuid })
expect(dupeHistoryOrError.isFailed()).to.equal(false)
let dupeHistory = dupeHistoryOrError.getValue()
if (dupeHistory.length !== expectedRevisions + 1) {
await Factory.sleep(Factory.ServerRevisionCreationDelay, 'No revisions found on the server. This is likely a delay issue. Retrying...')
const dupeHistoryOrError = await context.application.listRevisions.execute({ itemUuid: dupe.uuid })
expect(dupeHistoryOrError.isFailed()).to.equal(false)
dupeHistory = dupeHistoryOrError.getValue()
}
expect(dupeHistory.length).to.equal(expectedRevisions + 1)
}).timeout(Factory.SixtySecondTimeout)
it('can decrypt revisions for duplicate_of items', async function () {
const note = await Factory.createSyncedNote(application)
const note = await context.createSyncedNote('test note')
await Factory.sleep(Factory.ServerRevisionFrequency)
const changedText = `${Math.random()}`
await application.changeAndSaveItem.execute(note, (mutator) => {
mutator.title = changedText
})
await Factory.markDirtyAndSyncItem(application, note)
await context.changeNoteTitleAndSync(note, changedText)
await Factory.markDirtyAndSyncItem(context.application, note)
const dupe = await application.mutator.duplicateItem(note, true)
await Factory.markDirtyAndSyncItem(application, dupe)
const dupe = await context.application.mutator.duplicateItem(note, true)
await Factory.markDirtyAndSyncItem(context.application, dupe)
await Factory.sleep(Factory.ServerRevisionCreationDelay)
const itemHistoryOrError = await application.listRevisions.execute({ itemUuid: dupe.uuid })
const itemHistory = itemHistoryOrError.getValue()
const itemHistoryOrError = await context.application.listRevisions.execute({ itemUuid: dupe.uuid })
expect(itemHistoryOrError.isFailed()).to.equal(false)
let itemHistory = itemHistoryOrError.getValue()
if (itemHistory.length <= 1) {
await Factory.sleep(Factory.ServerRevisionCreationDelay, 'No revisions found on the server. This is likely a delay issue. Retrying...')
const itemHistoryOrError = await context.application.listRevisions.execute({ itemUuid: dupe.uuid })
expect(itemHistoryOrError.isFailed()).to.equal(false)
itemHistory = itemHistoryOrError.getValue()
}
expect(itemHistory.length).to.be.above(1)
const newestRevision = itemHistory[0]
const fetchedOrError = await application.getRevision.execute({
const fetchedOrError = await context.application.getRevision.execute({
itemUuid: dupe.uuid,
revisionUuid: newestRevision.uuid,
})
+22 -4
View File
@@ -41,7 +41,7 @@ export const acceptAllInvites = async (context) => {
}
}
const inviteContext = async (context, contactContext, sharedVault, contact, permission) => {
export const inviteContext = async (context, contactContext, sharedVault, contact, permission) => {
contactContext.lockSyncing()
const inviteOrError = await context.vaultInvites.inviteContactToSharedVault(sharedVault, contact, permission)
@@ -79,7 +79,11 @@ export const createSharedVaultWithAcceptedInvite = async (
await Utils.awaitPromiseOrThrow(promise, 2.0, 'Waiting for vault to sync')
const contactVault = contactContext.vaults.getVault({ keySystemIdentifier: sharedVault.systemIdentifier })
const contactVaultOrError = contactContext.vaults.getVault({ keySystemIdentifier: sharedVault.systemIdentifier })
if (contactVaultOrError.isFailed()) {
throw new Error(contactVaultOrError.getError())
}
const contactVault = contactVaultOrError.getValue()
return { sharedVault, contact, contactVault, contactContext, deinitContactContext }
}
@@ -198,7 +202,21 @@ export const createSharedVaultWithNote = async (context) => {
export const moveItemToVault = async (context, sharedVault, item) => {
const promise = context.resolveWhenItemCompletesAddingToVault(item)
const updatedItem = await context.vaults.moveItemToVault(sharedVault, item)
const result = await context.vaults.moveItemToVault(sharedVault, item)
await promise
return updatedItem
if (result.isFailed()) {
throw new Error(result.getError())
}
return result.getValue()
}
export const designateSharedVaultSurvior = async (context, sharedVault, survivorUuid) => {
const result = await context.vaultUsers.designateSurvivor(sharedVault, survivorUuid)
if (result.isFailed()) {
throw new Error(result.getError())
}
return result.getValue()
}
+5 -1
View File
@@ -112,7 +112,11 @@ export class VaultsContext extends AppContext {
await Collaboration.acceptAllInvites(thirdPartyContext)
const contactVault = thirdPartyContext.vaults.getVault({ keySystemIdentifier: sharedVault.systemIdentifier })
const contactVaultOrError = thirdPartyContext.vaults.getVault({ keySystemIdentifier: sharedVault.systemIdentifier })
if (contactVaultOrError.isFailed()) {
throw new Error(contactVaultOrError.getError())
}
const contactVault = contactVaultOrError.getValue()
return { contactVault, sharedVault, thirdPartyContext, deinitThirdPartyContext }
}
@@ -46,6 +46,7 @@ describe('online conflict handling', function () {
if (!application.dealloced) {
await context.deinit()
}
sinon.restore()
localStorage.clear()
})
@@ -991,4 +992,21 @@ describe('online conflict handling', function () {
await sharedFinalAssertions()
await contextB.deinit()
}).timeout(20000)
it('should not duplicate if saving item with invalid content type', async () => {
const payload = new DecryptedPayload({
uuid: Utils.generateUuid(),
content_type: 'SN|Privileges',
dirty: true,
content: {},
})
expectedItemCount++
await context.mutator.emitItemsFromPayloads([payload])
await context.sync()
const items = context.items.getAnyItems('SN|Privileges')
expect(items.length).to.equal(1)
await sharedFinalAssertions()
})
})
+18 -11
View File
@@ -38,6 +38,14 @@
<script type="module">
import MainRegistry from './TestRegistry/MainRegistry.js'
const urlSearchParams = new URLSearchParams(window.location.search);
const testSuite = urlSearchParams.get('suite');
if (testSuite === 'vaults') {
MainRegistry.VaultTests.exclusive = true;
} else if (testSuite === 'base') {
MainRegistry.BaseTests.exclusive = true;
}
const loadTest = (fileName) => {
return new Promise((resolve) => {
const script = document.createElement('script');
@@ -56,18 +64,17 @@
}
}
if (MainRegistry.VaultTests.enabled) {
InternalFeatureService.get().enableFeature(InternalFeature.Vaults);
if (MainRegistry.VaultTests.exclusive) {
await loadTests(MainRegistry.VaultTests.files);
} else {
await loadTests([
...MainRegistry.BaseTests,
...MainRegistry.VaultTests.files
]);
}
InternalFeatureService.get().enableFeature(InternalFeature.Vaults);
if (MainRegistry.VaultTests.exclusive) {
await loadTests(MainRegistry.VaultTests.files);
} else if (MainRegistry.BaseTests.exclusive) {
await loadTests(MainRegistry.BaseTests.files);
} else {
await loadTests(MainRegistry.BaseTests);
await loadTests([
...MainRegistry.BaseTests.files,
...MainRegistry.VaultTests.files
]);
}
mocha.run()
@@ -43,7 +43,7 @@ describe('asymmetric messages', function () {
contactContext.unlockSyncing()
await contactContext.syncAndAwaitMessageProcessing()
const updatedVault = contactContext.vaults.getVault({ keySystemIdentifier: sharedVault.systemIdentifier })
const updatedVault = contactContext.vaults.getVault({ keySystemIdentifier: sharedVault.systemIdentifier }).getValue()
expect(updatedVault.name).to.not.equal('new vault name')
expect(updatedVault.description).to.not.equal('new vault description')
@@ -225,7 +225,7 @@ describe('asymmetric messages', function () {
expect(firstPartySpy.callCount).to.equal(0)
expect(secondPartySpy.callCount).to.equal(1)
const updatedVault = contactContext.vaults.getVault({ keySystemIdentifier: sharedVault.systemIdentifier })
const updatedVault = contactContext.vaults.getVault({ keySystemIdentifier: sharedVault.systemIdentifier }).getValue()
expect(updatedVault.name).to.equal('New Name')
expect(updatedVault.description).to.equal('New Description')
@@ -268,7 +268,7 @@ describe('asymmetric messages', function () {
await contactContext.syncAndAwaitMessageProcessing()
const updatedVault = contactContext.vaults.getVault({ keySystemIdentifier: sharedVault.systemIdentifier })
const updatedVault = contactContext.vaults.getVault({ keySystemIdentifier: sharedVault.systemIdentifier }).getValue()
expect(updatedVault.name).to.equal('New Name')
expect(updatedVault.description).to.equal('New Description')
@@ -389,7 +389,7 @@ describe('asymmetric messages', function () {
const messages = await contactContext.asymmetric.getInboundMessages()
expect(messages.getValue().length).to.equal(0)
const updatedVault = contactContext.vaults.getVault({ keySystemIdentifier: sharedVault.systemIdentifier })
const updatedVault = contactContext.vaults.getVault({ keySystemIdentifier: sharedVault.systemIdentifier }).getValue()
expect(updatedVault.name).to.not.equal('New Name')
expect(updatedVault.description).to.not.equal('New Description')
+51 -7
View File
@@ -1,4 +1,5 @@
import * as Factory from '../lib/factory.js'
import * as Utils from '../lib/Utils.js'
import * as Collaboration from '../lib/Collaboration.js'
chai.use(chaiAsPromised)
@@ -109,14 +110,14 @@ describe('shared vault deletion', function () {
const originalNote = contactContext.items.findItem(note.uuid)
expect(originalNote).to.not.be.undefined
const contactVault = contactContext.vaults.getVault({ keySystemIdentifier: sharedVault.systemIdentifier })
const contactVault = contactContext.vaults.getVault({ keySystemIdentifier: sharedVault.systemIdentifier }).getValue()
await contactContext.vaultUsers.leaveSharedVault(contactVault)
const updatedContactNote = contactContext.items.findItem(note.uuid)
expect(updatedContactNote).to.be.undefined
const vault = await contactContext.vaults.getVault({ keySystemIdentifier: contactVault.systemIdentifier })
expect(vault).to.be.undefined
const vaultOrError = await contactContext.vaults.getVault({ keySystemIdentifier: contactVault.systemIdentifier })
expect(vaultOrError.isFailed()).to.be.true
await deinitContactContext()
})
@@ -142,13 +143,13 @@ describe('shared vault deletion', function () {
const { sharedVault, contactContext, deinitContactContext } =
await Collaboration.createSharedVaultWithAcceptedInvite(context)
const originalSharedVaultUsers = await context.vaultUsers.getSharedVaultUsers(sharedVault)
const originalSharedVaultUsers = await context.vaultUsers.getSharedVaultUsersFromServer(sharedVault)
expect(originalSharedVaultUsers.length).to.equal(2)
const result = await context.vaultUsers.removeUserFromSharedVault(sharedVault, contactContext.userUuid)
expect(result.isFailed()).to.be.false
const updatedSharedVaultUsers = await context.vaultUsers.getSharedVaultUsers(sharedVault)
const updatedSharedVaultUsers = await context.vaultUsers.getSharedVaultUsersFromServer(sharedVault)
expect(updatedSharedVaultUsers.length).to.equal(1)
await deinitContactContext()
@@ -162,9 +163,52 @@ describe('shared vault deletion', function () {
await contactContext.sync()
const vault = contactContext.vaults.getVault({ keySystemIdentifier: sharedVault.systemIdentifier })
expect(vault).to.be.undefined
const vaultOrError = contactContext.vaults.getVault({ keySystemIdentifier: sharedVault.systemIdentifier })
expect(vaultOrError.isFailed()).to.be.true
await deinitContactContext()
})
it('should remove a user from all shared vaults upon account removal', async () => {
const secondContext = await Factory.createVaultsContextWithRealCrypto()
await secondContext.launch()
await secondContext.register()
const thirdContext = await Factory.createVaultsContextWithRealCrypto()
await thirdContext.launch()
await thirdContext.register()
const firstVault = await Collaboration.createSharedVault(context)
const secondVault = await Collaboration.createSharedVault(thirdContext)
const firstToSecondContact = await Collaboration.createTrustedContactForUserOfContext(context, secondContext)
await Collaboration.createTrustedContactForUserOfContext(secondContext, context)
const thirdToSecondContact = await Collaboration.createTrustedContactForUserOfContext(thirdContext, secondContext)
await Collaboration.createTrustedContactForUserOfContext(thirdContext, context)
await Collaboration.inviteContext(context, secondContext, firstVault, firstToSecondContact, SharedVaultUserPermission.PERMISSIONS.Write)
await Collaboration.inviteContext(thirdContext, secondContext, secondVault, thirdToSecondContact, SharedVaultUserPermission.PERMISSIONS.Write)
const promise = secondContext.awaitNextSyncSharedVaultFromScratchEvent()
await Collaboration.acceptAllInvites(secondContext)
await Utils.awaitPromiseOrThrow(promise, 2.0, 'Waiting for vault to sync')
Factory.handlePasswordChallenges(secondContext.application, secondContext.password)
await secondContext.application.user.deleteAccount()
await context.syncAndAwaitNotificationsProcessing()
await thirdContext.syncAndAwaitNotificationsProcessing()
const sharedVaultUsersInFirstVault = await context.vaultUsers.getSharedVaultUsersFromServer(firstVault)
expect(sharedVaultUsersInFirstVault.length).to.equal(1)
const sharedVaultUsersInSecondVault = await thirdContext.vaultUsers.getSharedVaultUsersFromServer(secondVault)
expect(sharedVaultUsersInSecondVault.length).to.equal(1)
await secondContext.deinit()
await thirdContext.deinit()
})
})
+10 -15
View File
@@ -67,7 +67,7 @@ describe('shared vault files', function () {
const uploadedFile = await Files.uploadFile(context.files, buffer, 'my-file', 'md', 1000)
const sharedVault = await Collaboration.createSharedVault(context)
const addedFile = await context.vaults.moveItemToVault(sharedVault, uploadedFile)
const addedFile = await Collaboration.moveItemToVault(context, sharedVault, uploadedFile)
const downloadedBytes = await Files.downloadFile(context.files, addedFile)
expect(downloadedBytes).to.eql(buffer)
@@ -81,7 +81,7 @@ describe('shared vault files', function () {
const uploadedFile = await Files.uploadFile(context.files, buffer, 'my-file', 'md', 1000, firstVault)
const secondVault = await Collaboration.createSharedVault(context)
const movedFile = await context.vaults.moveItemToVault(secondVault, uploadedFile)
const movedFile = await Collaboration.moveItemToVault(context, secondVault, uploadedFile)
const downloadedBytes = await Files.downloadFile(context.files, movedFile)
expect(downloadedBytes).to.eql(buffer)
@@ -95,13 +95,13 @@ describe('shared vault files', function () {
const uploadedFile = await Files.uploadFile(context.files, buffer, 'my-file', 'md', 1000, firstVault)
const privateVault = await Collaboration.createPrivateVault(context)
const addedFile = await context.vaults.moveItemToVault(privateVault, uploadedFile)
const addedFile = await Collaboration.moveItemToVault(context, privateVault, uploadedFile)
const downloadedBytes = await Files.downloadFile(context.files, addedFile)
expect(downloadedBytes).to.eql(buffer)
})
it('moving a note to a vault should also moved linked files', async () => {
it('should not move a note to a vault that is linked with files ', async () => {
const note = await context.createSyncedNote()
const response = await fetch('/mocha/assets/small_file.md')
const buffer = new Uint8Array(await response.arrayBuffer())
@@ -111,17 +111,12 @@ describe('shared vault files', function () {
const sharedVault = await Collaboration.createSharedVault(context)
context.vaults.alerts.confirmV2 = () => Promise.resolve(true)
context.vaults.alerts.alertV2 = () => Promise.resolve(true)
await context.vaults.moveItemToVault(sharedVault, note)
const latestFile = context.items.findItem(updatedFile.uuid)
expect(context.vaults.getItemVault(latestFile).uuid).to.equal(sharedVault.uuid)
expect(context.vaults.getItemVault(context.items.findItem(note.uuid)).uuid).to.equal(sharedVault.uuid)
const downloadedBytes = await Files.downloadFile(context.files, latestFile)
expect(downloadedBytes).to.eql(buffer)
await Factory.expectThrowsAsync(
() => Collaboration.moveItemToVault(context, sharedVault, note),
'This item is linked to other items that are not in the same vault. Please move those items to this vault first.',
)
})
it('should be able to move a file out of its vault', async () => {
@@ -225,7 +220,7 @@ describe('shared vault files', function () {
const response = await fetch('/mocha/assets/small_file.md')
const buffer = new Uint8Array(await response.arrayBuffer())
const uploadedFile = await Files.uploadFile(context.files, buffer, 'my-file', 'md', 1000)
const addedFile = await context.vaults.moveItemToVault(sharedVault, uploadedFile)
const addedFile = await Collaboration.moveItemToVault(context, sharedVault, uploadedFile)
await contactContext.sync()
+22 -8
View File
@@ -70,25 +70,39 @@ describe('shared vault invites', function () {
await deinitContactContext()
})
it('invite should include delegated trusted contacts', async () => {
it('invite should include delegated trusted contacts and add them when accepted', async () => {
const { sharedVault, contactContext, deinitContactContext } =
await Collaboration.createSharedVaultWithAcceptedInvite(context)
const { thirdPartyContext, deinitThirdPartyContext } = await Collaboration.inviteNewPartyToSharedVault(
context,
sharedVault,
)
const { thirdPartyContext: party1Context, deinitThirdPartyContext: deinitParty1Context } =
await Collaboration.inviteNewPartyToSharedVault(context, sharedVault)
const invites = thirdPartyContext.vaultInvites.getCachedPendingInviteRecords()
await Collaboration.acceptAllInvites(party1Context)
const { thirdPartyContext: party2Context, deinitThirdPartyContext: deinitParty2Context } =
await Collaboration.inviteNewPartyToSharedVault(context, sharedVault)
const invites = party2Context.vaultInvites.getCachedPendingInviteRecords()
const message = invites[0].message
const delegatedContacts = message.data.trustedContacts
expect(delegatedContacts.length).to.equal(2)
expect(delegatedContacts.length).to.equal(3)
expect(delegatedContacts.some((contact) => contact.contactUuid === context.userUuid)).to.be.true
expect(delegatedContacts.some((contact) => contact.contactUuid === contactContext.userUuid)).to.be.true
expect(delegatedContacts.some((contact) => contact.contactUuid === party1Context.userUuid)).to.be.true
await deinitThirdPartyContext()
await Collaboration.acceptAllInvites(party2Context)
const trustedContacts = party2Context.contacts.getAllContacts()
expect(trustedContacts.length).to.equal(4)
expect(trustedContacts.some((contact) => contact.contactUuid === context.userUuid)).to.be.true
expect(trustedContacts.some((contact) => contact.contactUuid === contactContext.userUuid)).to.be.true
expect(trustedContacts.some((contact) => contact.contactUuid === party1Context.userUuid)).to.be.true
await deinitParty1Context()
await deinitParty2Context()
await deinitContactContext()
})
@@ -59,7 +59,7 @@ describe('vault key management', function () {
await Factory.expectThrowsAsync(
() => context.vaults.removeItemFromVault(item),
'Cannot find latest version of item to get vault for',
'Cannot find vault to remove item from',
)
})
@@ -474,7 +474,7 @@ describe('vault key management', function () {
const storedKey = context.keys.getRootKeyFromStorageForVault(vault.systemIdentifier)
expect(storedKey).to.be.undefined
const updatedVault = context.vaults.getVault({ keySystemIdentifier: vault.systemIdentifier })
const updatedVault = context.vaults.getVault({ keySystemIdentifier: vault.systemIdentifier }).getValue()
expect(updatedVault.keyStorageMode).to.equal(KeySystemRootKeyStorageMode.Synced)
})
@@ -127,7 +127,7 @@ describe('vault key rotation', function () {
contactContext.unlockSyncing()
await contactContext.sync()
const vault = await contactContext.vaults.getVault({ keySystemIdentifier: sharedVault.systemIdentifier })
const vault = await contactContext.vaults.getVault({ keySystemIdentifier: sharedVault.systemIdentifier }).getValue()
expect(vault.rootKeyParams).to.eql(rootKey.keyParams)
await deinitContactContext()
@@ -101,7 +101,7 @@ describe('keypair change', function () {
contactContext.unlockSyncing()
await contactContext.syncAndAwaitMessageProcessing()
const updatedVault = contactContext.vaults.getVault({ keySystemIdentifier: sharedVault.systemIdentifier })
const updatedVault = contactContext.vaults.getVault({ keySystemIdentifier: sharedVault.systemIdentifier }).getValue()
expect(updatedVault.name).to.equal(sharedVault.name)
expect(updatedVault.description).to.equal(sharedVault.description)
expect(updatedVault.name).to.not.equal('New Name')
+3 -3
View File
@@ -43,7 +43,7 @@ describe('shared vault limits', function () {
describe('plus users', () => {
it('should not allow creating vaults over the limit', async () => {
context.activatePaidSubscriptionForUser({ subscriptionPlanName: 'PLUS_PLAN' })
await context.activatePaidSubscriptionForUser({ subscriptionPlanName: 'PLUS_PLAN' })
for (let i = 0; i < 3; i++) {
const vault = await Collaboration.createSharedVault(context)
@@ -63,12 +63,12 @@ describe('shared vault limits', function () {
describe('pro users', () => {
it('should allow creating vaults without limit', async () => {
context.activatePaidSubscriptionForUser()
await context.activatePaidSubscriptionForUser()
for (let i = 0; i < 10; i++) {
const vault = await Collaboration.createSharedVault(context)
expect(vault).to.not.be.null
}
})
}).timeout(Factory.SixtySecondTimeout)
})
})
+7 -7
View File
@@ -37,7 +37,7 @@ describe('shared vault quota', function () {
await context.syncAndAwaitNotificationsProcessing()
const updatedVault = context.vaults.getVault({ keySystemIdentifier: sharedVault.systemIdentifier })
const updatedVault = context.vaults.getVault({ keySystemIdentifier: sharedVault.systemIdentifier }).getValue()
expect(updatedVault.sharing.fileBytesUsed).to.equal(1374)
const bytesUsedSetting = await context.application.settings.getSubscriptionSetting(
@@ -80,7 +80,7 @@ describe('shared vault quota', function () {
await context.syncAndAwaitNotificationsProcessing()
const updatedVault = context.vaults.getVault({ keySystemIdentifier: sharedVault.systemIdentifier })
const updatedVault = context.vaults.getVault({ keySystemIdentifier: sharedVault.systemIdentifier }).getValue()
expect(updatedVault.sharing.fileBytesUsed).to.equal(1374)
const bytesUsedSetting = await context.application.settings.getSubscriptionSetting(
@@ -106,7 +106,7 @@ describe('shared vault quota', function () {
await context.syncAndAwaitNotificationsProcessing()
await contactContext.syncAndAwaitNotificationsProcessing()
const updatedVault = context.vaults.getVault({ keySystemIdentifier: sharedVault.systemIdentifier })
const updatedVault = context.vaults.getVault({ keySystemIdentifier: sharedVault.systemIdentifier }).getValue()
expect(updatedVault.sharing.fileBytesUsed).to.equal(1374)
const myBytesUsedSetting = await context.application.settings.getSubscriptionSetting(
@@ -170,10 +170,10 @@ describe('shared vault quota', function () {
await context.syncAndAwaitNotificationsProcessing()
await contactContext.syncAndAwaitNotificationsProcessing()
let updatedSharedVault = context.vaults.getVault({ keySystemIdentifier: sharedVault.systemIdentifier })
let updatedSharedVault = context.vaults.getVault({ keySystemIdentifier: sharedVault.systemIdentifier }).getValue()
expect(updatedSharedVault.sharing.fileBytesUsed).to.equal(0)
let updatedSecondVault = contactContext.vaults.getVault({ keySystemIdentifier: secondVault.systemIdentifier })
let updatedSecondVault = contactContext.vaults.getVault({ keySystemIdentifier: secondVault.systemIdentifier }).getValue()
expect(updatedSecondVault.sharing.fileBytesUsed).to.equal(1374)
let myBytesUsedSetting = await context.application.settings.getSubscriptionSetting(
@@ -191,10 +191,10 @@ describe('shared vault quota', function () {
await context.syncAndAwaitNotificationsProcessing()
await contactContext.syncAndAwaitNotificationsProcessing()
updatedSharedVault = context.vaults.getVault({ keySystemIdentifier: sharedVault.systemIdentifier })
updatedSharedVault = context.vaults.getVault({ keySystemIdentifier: sharedVault.systemIdentifier }).getValue()
expect(updatedSharedVault.sharing.fileBytesUsed).to.equal(1374)
updatedSecondVault = contactContext.vaults.getVault({ keySystemIdentifier: secondVault.systemIdentifier })
updatedSecondVault = contactContext.vaults.getVault({ keySystemIdentifier: secondVault.systemIdentifier }).getValue()
expect(updatedSecondVault.sharing.fileBytesUsed).to.equal(0)
myBytesUsedSetting = await context.application.settings.getSubscriptionSetting(
+70 -14
View File
@@ -46,11 +46,29 @@ describe('shared vault revisions', function () {
await Factory.sleep(Factory.ServerRevisionCreationDelay)
const contactItemHistoryOrError = await contactContext.application.listRevisions.execute({ itemUuid: note.uuid })
const contactItemHistory = contactItemHistoryOrError.getValue()
expect(contactItemHistoryOrError.isFailed()).to.equal(false)
let contactItemHistory = contactItemHistoryOrError.getValue()
if (contactItemHistory.length < 2) {
await Factory.sleep(Factory.ServerRevisionCreationDelay, 'Not enough revisions found on the server. This is likely a delay issue. Retrying...')
const contactItemHistoryOrError = await contactContext.application.listRevisions.execute({ itemUuid: note.uuid })
expect(contactItemHistoryOrError.isFailed()).to.equal(false)
contactItemHistory = contactItemHistoryOrError.getValue()
}
expect(contactItemHistory.length >= 2).to.be.true
const itemHistoryOrError = await context.application.listRevisions.execute({ itemUuid: note.uuid })
const itemHistory = itemHistoryOrError.getValue()
expect(itemHistoryOrError.isFailed()).to.equal(false)
let itemHistory = itemHistoryOrError.getValue()
if (itemHistory.length < 2) {
await Factory.sleep(Factory.ServerRevisionCreationDelay, 'Not enough revisions found on the server. This is likely a delay issue. Retrying...')
const itemHistoryOrError = await context.application.listRevisions.execute({ itemUuid: note.uuid })
expect(itemHistoryOrError.isFailed()).to.equal(false)
itemHistory = itemHistoryOrError.getValue()
}
expect(itemHistory.length >= 2).to.be.true
})
@@ -67,7 +85,16 @@ describe('shared vault revisions', function () {
await Factory.sleep(Factory.ServerRevisionCreationDelay)
const itemHistoryOrError = await context.application.listRevisions.execute({ itemUuid: note.uuid })
const itemHistory = itemHistoryOrError.getValue()
expect(itemHistoryOrError.isFailed()).to.equal(false)
let itemHistory = itemHistoryOrError.getValue()
if (itemHistory.length < 2) {
await Factory.sleep(Factory.ServerRevisionCreationDelay, 'Not enough revisions found on the server. This is likely a delay issue. Retrying...')
const itemHistoryOrError = await context.application.listRevisions.execute({ itemUuid: note.uuid })
expect(itemHistoryOrError.isFailed()).to.equal(false)
itemHistory = itemHistoryOrError.getValue()
}
expect(itemHistory.length >= 2).to.be.true
})
@@ -82,7 +109,16 @@ describe('shared vault revisions', function () {
await Factory.sleep(Factory.ServerRevisionCreationDelay)
const itemHistoryOrError = await contactContext.application.listRevisions.execute({ itemUuid: note.uuid })
const itemHistory = itemHistoryOrError.getValue()
expect(itemHistoryOrError.isFailed()).to.equal(false)
let itemHistory = itemHistoryOrError.getValue()
if (itemHistory.length < 1) {
await Factory.sleep(Factory.ServerRevisionCreationDelay, 'Not enough revisions found on the server. This is likely a delay issue. Retrying...')
const itemHistoryOrError = await contactContext.application.listRevisions.execute({ itemUuid: note.uuid })
expect(itemHistoryOrError.isFailed()).to.equal(false)
itemHistory = itemHistoryOrError.getValue()
}
const newestRevision = itemHistory[0]
const fetchedOrError = await contactContext.application.getRevision.execute({
@@ -110,9 +146,16 @@ describe('shared vault revisions', function () {
await Factory.sleep(Factory.ServerRevisionCreationDelay)
const itemHistoryOrError = await contactContext.application.listRevisions.execute({ itemUuid: note.uuid })
expect(itemHistoryOrError.isFailed()).to.be.false
expect(itemHistoryOrError.isFailed()).to.equal(false)
let itemHistory = itemHistoryOrError.getValue()
if (itemHistory.length != 0) {
await Factory.sleep(Factory.ServerRevisionCreationDelay, 'Not enough revisions found on the server. This is likely a delay issue. Retrying...')
const itemHistory = itemHistoryOrError.getValue()
const itemHistoryOrError = await contactContext.application.listRevisions.execute({ itemUuid: note.uuid })
expect(itemHistoryOrError.isFailed()).to.equal(false)
itemHistory = itemHistoryOrError.getValue()
}
expect(itemHistory.length).to.equal(0)
})
@@ -127,19 +170,32 @@ describe('shared vault revisions', function () {
await Factory.sleep(Factory.ServerRevisionCreationDelay)
let itemHistoryOrError = await contactContext.application.listRevisions.execute({ itemUuid: note.uuid })
expect(itemHistoryOrError.isFailed()).to.be.false
let itemHistory = itemHistoryOrError.getValue()
const itemHistoryBeforeOrError = await contactContext.application.listRevisions.execute({ itemUuid: note.uuid })
expect(itemHistoryBeforeOrError.isFailed()).to.equal(false)
let itemHistoryBefore = itemHistoryBeforeOrError.getValue()
if (itemHistoryBefore.length < 1) {
await Factory.sleep(Factory.ServerRevisionCreationDelay, 'Not enough revisions found on the server. This is likely a delay issue. Retrying...')
expect(itemHistory.length >= 1).to.be.true
const itemHistoryOrError = await contactContext.application.listRevisions.execute({ itemUuid: note.uuid })
expect(itemHistoryOrError.isFailed()).to.equal(false)
itemHistoryBefore = itemHistoryOrError.getValue()
}
expect(itemHistoryBefore.length >= 1).to.be.true
await context.vaultUsers.removeUserFromSharedVault(sharedVault, contactContext.userUuid)
await Factory.sleep(Factory.ServerRevisionCreationDelay)
itemHistoryOrError = await contactContext.application.listRevisions.execute({ itemUuid: note.uuid })
expect(itemHistoryOrError.isFailed()).to.be.false
itemHistory = itemHistoryOrError.getValue()
const itemHistoryAfterOrError = await contactContext.application.listRevisions.execute({ itemUuid: note.uuid })
let itemHistoryAfter = itemHistoryAfterOrError.getValue()
if (itemHistoryAfter.length != 0) {
await Factory.sleep(Factory.ServerRevisionCreationDelay, 'Not enough revisions found on the server. This is likely a delay issue. Retrying...')
expect(itemHistory.length).to.equal(0)
const itemHistoryOrError = await contactContext.application.listRevisions.execute({ itemUuid: note.uuid })
expect(itemHistoryOrError.isFailed()).to.equal(false)
itemHistoryAfter = itemHistoryOrError.getValue()
}
expect(itemHistoryAfter.length).to.equal(0)
})
})
@@ -36,14 +36,14 @@ describe('shared vaults', function () {
description: 'new vault description',
})
const updatedVault = context.vaults.getVault({ keySystemIdentifier: sharedVault.systemIdentifier })
const updatedVault = context.vaults.getVault({ keySystemIdentifier: sharedVault.systemIdentifier }).getValue()
expect(updatedVault.name).to.equal('new vault name')
expect(updatedVault.description).to.equal('new vault description')
contactContext.unlockSyncing()
await contactContext.syncAndAwaitMessageProcessing()
const contactVault = contactContext.vaults.getVault({ keySystemIdentifier: sharedVault.systemIdentifier })
const contactVault = contactContext.vaults.getVault({ keySystemIdentifier: sharedVault.systemIdentifier }).getValue()
expect(contactVault.name).to.equal('new vault name')
expect(contactVault.description).to.equal('new vault description')
@@ -59,14 +59,14 @@ describe('shared vaults', function () {
await contactContext.syncAndAwaitNotificationsProcessing()
expect(contactContext.vaults.getVault({ keySystemIdentifier: sharedVault.systemIdentifier })).to.be.undefined
expect(contactContext.vaults.getVault({ keySystemIdentifier: sharedVault.systemIdentifier }).isFailed()).to.be.true
expect(contactContext.keys.getPrimaryKeySystemRootKey(sharedVault.systemIdentifier)).to.be.undefined
expect(contactContext.keys.getKeySystemItemsKeys(sharedVault.systemIdentifier)).to.be.empty
const recreatedContext = await Factory.createVaultsContextWithRealCrypto(contactContext.identifier)
await recreatedContext.launch()
expect(recreatedContext.vaults.getVault({ keySystemIdentifier: sharedVault.systemIdentifier })).to.be.undefined
expect(recreatedContext.vaults.getVault({ keySystemIdentifier: sharedVault.systemIdentifier }).isFailed()).to.be.true
expect(recreatedContext.keys.getPrimaryKeySystemRootKey(sharedVault.systemIdentifier)).to.be.undefined
expect(recreatedContext.keys.getKeySystemItemsKeys(sharedVault.systemIdentifier)).to.be.empty
@@ -84,14 +84,14 @@ describe('shared vaults', function () {
await contactContext.syncAndAwaitNotificationsProcessing()
expect(contactContext.vaults.getVault({ keySystemIdentifier: sharedVault.systemIdentifier })).to.be.undefined
expect(contactContext.vaults.getVault({ keySystemIdentifier: sharedVault.systemIdentifier }).isFailed()).to.be.true
expect(contactContext.keys.getPrimaryKeySystemRootKey(sharedVault.systemIdentifier)).to.be.undefined
expect(contactContext.keys.getKeySystemItemsKeys(sharedVault.systemIdentifier)).to.be.empty
const recreatedContext = await Factory.createVaultsContextWithRealCrypto(contactContext.identifier)
await recreatedContext.launch()
expect(recreatedContext.vaults.getVault({ keySystemIdentifier: sharedVault.systemIdentifier })).to.be.undefined
expect(recreatedContext.vaults.getVault({ keySystemIdentifier: sharedVault.systemIdentifier }).isFailed()).to.be.true
expect(recreatedContext.keys.getPrimaryKeySystemRootKey(sharedVault.systemIdentifier)).to.be.undefined
expect(recreatedContext.keys.getKeySystemItemsKeys(sharedVault.systemIdentifier)).to.be.empty
@@ -0,0 +1,363 @@
import * as Factory from '../lib/factory.js'
import * as Files from '../lib/Files.js'
import * as Collaboration from '../lib/Collaboration.js'
chai.use(chaiAsPromised)
const expect = chai.expect
describe('designated survival', function () {
this.timeout(Factory.ThirtySecondTimeout)
let context
let secondContext
let thirdContext
beforeEach(async function () {
localStorage.clear()
context = await Factory.createVaultsContextWithRealCrypto()
await context.launch()
await context.register()
})
afterEach(async function () {
await context.deinit()
localStorage.clear()
sinon.restore()
context = undefined
if (secondContext) {
await secondContext.deinit()
secondContext = undefined
}
if (thirdContext) {
await thirdContext.deinit()
thirdContext = undefined
}
})
it('should indicate that a vault has a designated survivor', async () => {
const { sharedVault, contactContext } =
await Collaboration.createSharedVaultWithAcceptedInvite(context)
secondContext = contactContext
let vault = context.vaults.getVault({ keySystemIdentifier: sharedVault.systemIdentifier }).getValue()
expect(vault.sharing.designatedSurvivor).to.be.null
await Collaboration.designateSharedVaultSurvior(context, sharedVault, contactContext.userUuid)
await context.syncAndAwaitNotificationsProcessing()
vault = context.vaults.getVault({ keySystemIdentifier: sharedVault.systemIdentifier }).getValue()
expect(vault.sharing.designatedSurvivor).to.equal(contactContext.userUuid)
})
describe.skip('owner of a shared vault with a designated survivor removing the vault', () => {
it('should not remove all users from the vault upon shared vault removal', async () => {
const { sharedVault, contactContext } =
await Collaboration.createSharedVaultWithAcceptedInvite(context)
secondContext = contactContext
await Collaboration.designateSharedVaultSurvior(context, sharedVault, contactContext.userUuid)
const { thirdPartyContext } = await Collaboration.inviteNewPartyToSharedVault(
context,
sharedVault,
)
thirdContext = thirdPartyContext
await Collaboration.acceptAllInvites(thirdContext)
await context.sharedVaults.deleteSharedVault(sharedVault)
await context.syncAndAwaitNotificationsProcessing()
await secondContext.syncAndAwaitNotificationsProcessing()
await thirdContext.syncAndAwaitNotificationsProcessing()
const sharedVaultUsers = await secondContext.vaultUsers.getSharedVaultUsersFromServer(sharedVault)
expect(sharedVaultUsers.length).to.equal(2)
expect(context.vaults.getVault({ keySystemIdentifier: sharedVault.systemIdentifier }).isFailed()).to.be.true
expect(context.keys.getPrimaryKeySystemRootKey(sharedVault.systemIdentifier)).to.be.undefined
expect(context.keys.getKeySystemItemsKeys(sharedVault.systemIdentifier)).to.be.empty
expect(secondContext.vaults.getVault({ keySystemIdentifier: sharedVault.systemIdentifier }).getValue()).to.not.be.undefined
expect(secondContext.keys.getPrimaryKeySystemRootKey(sharedVault.systemIdentifier)).to.not.be.undefined
expect(secondContext.keys.getKeySystemItemsKeys(sharedVault.systemIdentifier)).to.not.be.empty
expect(thirdContext.vaults.getVault({ keySystemIdentifier: sharedVault.systemIdentifier }).getValue()).to.not.be.undefined
expect(thirdContext.keys.getPrimaryKeySystemRootKey(sharedVault.systemIdentifier)).to.not.be.undefined
expect(thirdContext.keys.getKeySystemItemsKeys(sharedVault.systemIdentifier)).to.not.be.empty
})
it('should transition items of the owner in the vault to the designated survivor', async () => {
const { sharedVault, contactContext } =
await Collaboration.createSharedVaultWithAcceptedInvite(context)
secondContext = contactContext
await Collaboration.designateSharedVaultSurvior(context, sharedVault, contactContext.userUuid)
const { thirdPartyContext } = await Collaboration.inviteNewPartyToSharedVault(
context,
sharedVault,
)
thirdContext = thirdPartyContext
await Collaboration.acceptAllInvites(thirdContext)
const note = await context.createSyncedNote('foo', 'bar')
await Collaboration.moveItemToVault(context, sharedVault, note)
await context.sharedVaults.deleteSharedVault(sharedVault)
await secondContext.syncAndAwaitNotificationsProcessing()
await thirdContext.syncAndAwaitNotificationsProcessing()
const sharedVaultUsers = await secondContext.vaultUsers.getSharedVaultUsersFromServer(sharedVault)
expect(sharedVaultUsers.length).to.equal(2)
const contactNote = secondContext.items.findItem(note.uuid)
expect(contactNote.key_system_identifier).to.equal(sharedVault.systemIdentifier)
expect(contactNote.user_uuid).to.equal(secondContext.userUuid)
const thirdPartyNote = thirdContext.items.findItem(note.uuid)
expect(thirdPartyNote.key_system_identifier).to.equal(sharedVault.systemIdentifier)
expect(thirdPartyNote.user_uuid).to.equal(secondContext.userUuid)
})
it('should still allow to download files of the owner in the vault', async () => {
await context.activatePaidSubscriptionForUser()
const { sharedVault, contactContext } =
await Collaboration.createSharedVaultWithAcceptedInvite(context)
secondContext = contactContext
await Collaboration.designateSharedVaultSurvior(context, sharedVault, contactContext.userUuid)
const { thirdPartyContext } = await Collaboration.inviteNewPartyToSharedVault(
context,
sharedVault,
)
thirdContext = thirdPartyContext
await Collaboration.acceptAllInvites(thirdContext)
const response = await fetch('/mocha/assets/small_file.md')
const buffer = new Uint8Array(await response.arrayBuffer())
const uploadedFile = await Files.uploadFile(context.files, buffer, 'my-file', 'md', 1000, sharedVault)
await secondContext.syncAndAwaitNotificationsProcessing()
const sharedFileBefore = secondContext.items.findItem(uploadedFile.uuid)
expect(sharedFileBefore).to.not.be.undefined
expect(sharedFileBefore.remoteIdentifier).to.equal(uploadedFile.remoteIdentifier)
await context.sharedVaults.deleteSharedVault(sharedVault)
await secondContext.syncAndAwaitNotificationsProcessing()
const sharedFileAfter = secondContext.items.findItem(uploadedFile.uuid)
expect(sharedFileAfter).to.not.be.undefined
expect(sharedFileAfter.remoteIdentifier).to.equal(uploadedFile.remoteIdentifier)
const downloadedBytes = await Files.downloadFile(secondContext.files, sharedFileAfter)
expect(downloadedBytes).to.eql(buffer)
})
it('should transition vault ownership to the designated survivor', async () => {
const { sharedVault, contactContext } =
await Collaboration.createSharedVaultWithAcceptedInvite(context)
secondContext = contactContext
await Collaboration.designateSharedVaultSurvior(context, sharedVault, contactContext.userUuid)
const { thirdPartyContext } = await Collaboration.inviteNewPartyToSharedVault(
context,
sharedVault,
)
thirdContext = thirdPartyContext
await Collaboration.acceptAllInvites(thirdContext)
await context.sharedVaults.deleteSharedVault(sharedVault)
await secondContext.syncAndAwaitNotificationsProcessing()
await thirdContext.syncAndAwaitNotificationsProcessing()
const contactVault = secondContext.vaults.getVault({ keySystemIdentifier: sharedVault.systemIdentifier }).getValue()
expect(contactVault.sharing.ownerUserUuid).to.not.equal(context.userUuid)
expect(contactVault.sharing.ownerUserUuid).to.equal(secondContext.userUuid)
const thirdPartyVault = thirdContext.vaults.getVault({ keySystemIdentifier: sharedVault.systemIdentifier }).getValue()
expect(thirdPartyVault.sharing.ownerUserUuid).to.not.equal(context.userUuid)
expect(thirdPartyVault.sharing.ownerUserUuid).to.equal(secondContext.userUuid)
})
})
describe('owner of a shared vault with a designated survivor deleting their account', () => {
it('should not remove all users from the vault upon account removal', async () => {
const { sharedVault, contactContext } =
await Collaboration.createSharedVaultWithAcceptedInvite(context)
secondContext = contactContext
await Collaboration.designateSharedVaultSurvior(context, sharedVault, contactContext.userUuid)
const { thirdPartyContext } = await Collaboration.inviteNewPartyToSharedVault(
context,
sharedVault,
)
thirdContext = thirdPartyContext
await Collaboration.acceptAllInvites(thirdContext)
Factory.handlePasswordChallenges(context.application, context.password)
await context.application.user.deleteAccount()
await secondContext.syncAndAwaitNotificationsProcessing()
await thirdContext.syncAndAwaitNotificationsProcessing()
const sharedVaultUsers = await secondContext.vaultUsers.getSharedVaultUsersFromServer(sharedVault)
expect(sharedVaultUsers.length).to.equal(2)
expect(secondContext.vaults.getVault({ keySystemIdentifier: sharedVault.systemIdentifier }).getValue()).to.not.be.undefined
expect(secondContext.keys.getPrimaryKeySystemRootKey(sharedVault.systemIdentifier)).to.not.be.undefined
expect(secondContext.keys.getKeySystemItemsKeys(sharedVault.systemIdentifier)).to.not.be.empty
expect(thirdContext.vaults.getVault({ keySystemIdentifier: sharedVault.systemIdentifier }).getValue()).to.not.be.undefined
expect(thirdContext.keys.getPrimaryKeySystemRootKey(sharedVault.systemIdentifier)).to.not.be.undefined
expect(thirdContext.keys.getKeySystemItemsKeys(sharedVault.systemIdentifier)).to.not.be.empty
})
it('should transition items of the owner in the vault to the designated survivor', async () => {
const { sharedVault, contactContext } =
await Collaboration.createSharedVaultWithAcceptedInvite(context)
secondContext = contactContext
await Collaboration.designateSharedVaultSurvior(context, sharedVault, contactContext.userUuid)
const { thirdPartyContext } = await Collaboration.inviteNewPartyToSharedVault(
context,
sharedVault,
)
thirdContext = thirdPartyContext
await Collaboration.acceptAllInvites(thirdContext)
const note = await context.createSyncedNote('foo', 'bar')
await Collaboration.moveItemToVault(context, sharedVault, note)
Factory.handlePasswordChallenges(context.application, context.password)
await context.application.user.deleteAccount()
await secondContext.syncAndAwaitNotificationsProcessing()
await thirdContext.syncAndAwaitNotificationsProcessing()
const sharedVaultUsers = await secondContext.vaultUsers.getSharedVaultUsersFromServer(sharedVault)
expect(sharedVaultUsers.length).to.equal(2)
const contactNote = secondContext.items.findItem(note.uuid)
expect(contactNote.key_system_identifier).to.equal(sharedVault.systemIdentifier)
expect(contactNote.user_uuid).to.equal(secondContext.userUuid)
const thirdPartyNote = thirdContext.items.findItem(note.uuid)
expect(thirdPartyNote.key_system_identifier).to.equal(sharedVault.systemIdentifier)
expect(thirdPartyNote.user_uuid).to.equal(secondContext.userUuid)
})
it('should still allow to download files of the owner in the vault', async () => {
await context.activatePaidSubscriptionForUser()
const { sharedVault, contactContext } =
await Collaboration.createSharedVaultWithAcceptedInvite(context)
secondContext = contactContext
await Collaboration.designateSharedVaultSurvior(context, sharedVault, contactContext.userUuid)
const { thirdPartyContext } = await Collaboration.inviteNewPartyToSharedVault(
context,
sharedVault,
)
thirdContext = thirdPartyContext
await Collaboration.acceptAllInvites(thirdContext)
const response = await fetch('/mocha/assets/small_file.md')
const buffer = new Uint8Array(await response.arrayBuffer())
const uploadedFile = await Files.uploadFile(context.files, buffer, 'my-file', 'md', 1000, sharedVault)
await secondContext.syncAndAwaitNotificationsProcessing()
const sharedFileBefore = secondContext.items.findItem(uploadedFile.uuid)
expect(sharedFileBefore).to.not.be.undefined
expect(sharedFileBefore.remoteIdentifier).to.equal(uploadedFile.remoteIdentifier)
Factory.handlePasswordChallenges(context.application, context.password)
await context.application.user.deleteAccount()
await secondContext.syncAndAwaitNotificationsProcessing()
const sharedFileAfter = secondContext.items.findItem(uploadedFile.uuid)
expect(sharedFileAfter).to.not.be.undefined
expect(sharedFileAfter.remoteIdentifier).to.equal(uploadedFile.remoteIdentifier)
const downloadedBytes = await Files.downloadFile(secondContext.files, sharedFileAfter)
expect(downloadedBytes).to.eql(buffer)
})
it('should transition vault ownership to the designated survivor', async () => {
const { sharedVault, contactContext } =
await Collaboration.createSharedVaultWithAcceptedInvite(context)
secondContext = contactContext
await Collaboration.designateSharedVaultSurvior(context, sharedVault, contactContext.userUuid)
const { thirdPartyContext } = await Collaboration.inviteNewPartyToSharedVault(
context,
sharedVault,
)
thirdContext = thirdPartyContext
await Collaboration.acceptAllInvites(thirdContext)
Factory.handlePasswordChallenges(context.application, context.password)
await context.application.user.deleteAccount()
await secondContext.syncAndAwaitNotificationsProcessing()
await thirdContext.syncAndAwaitNotificationsProcessing()
const contactVault = secondContext.vaults.getVault({ keySystemIdentifier: sharedVault.systemIdentifier }).getValue()
expect(contactVault.sharing.ownerUserUuid).to.equal(secondContext.userUuid)
const thirdPartyVault = thirdContext.vaults.getVault({ keySystemIdentifier: sharedVault.systemIdentifier }).getValue()
expect(thirdPartyVault.sharing.ownerUserUuid).to.equal(secondContext.userUuid)
})
})
describe('owner of a shared vault without a designated survivor deleting their account', () => {
it('should remove all users from all shared vaults upon account removal', async () => {
await context.activatePaidSubscriptionForUser()
const { sharedVault, contactContext } =
await Collaboration.createSharedVaultWithAcceptedInvite(context)
secondContext = contactContext
const result = await Collaboration.createSharedVaultWithAcceptedInvite(context)
thirdContext = result.contactContext
const secondSharedVault = result.sharedVault
Factory.handlePasswordChallenges(context.application, context.password)
await context.application.user.deleteAccount()
await secondContext.syncAndAwaitNotificationsProcessing()
await thirdContext.syncAndAwaitNotificationsProcessing()
expect(secondContext.vaults.getVault({ keySystemIdentifier: sharedVault.systemIdentifier }).isFailed()).to.be.true
expect(secondContext.keys.getPrimaryKeySystemRootKey(sharedVault.systemIdentifier)).to.be.undefined
expect(secondContext.keys.getKeySystemItemsKeys(sharedVault.systemIdentifier)).to.be.empty
expect(thirdContext.vaults.getVault({ keySystemIdentifier: secondSharedVault.systemIdentifier }).isFailed()).to.be.true
expect(thirdContext.keys.getPrimaryKeySystemRootKey(secondSharedVault.systemIdentifier)).to.be.undefined
expect(thirdContext.keys.getKeySystemItemsKeys(secondSharedVault.systemIdentifier)).to.be.empty
})
})
})
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/snjs",
"version": "2.202.76",
"version": "2.202.92",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
@@ -37,7 +37,7 @@
"@babel/preset-env": "*",
"@standardnotes/api": "workspace:*",
"@standardnotes/common": "^1.50.0",
"@standardnotes/domain-core": "^1.28.0",
"@standardnotes/domain-core": "^1.34.1",
"@standardnotes/domain-events": "^2.122.0",
"@standardnotes/encryption": "workspace:*",
"@standardnotes/features": "workspace:*",
+40
View File
@@ -3,6 +3,46 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.31.25](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-28)
**Note:** Version bump only for package @standardnotes/ui-services
## [1.31.24](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-26)
**Note:** Version bump only for package @standardnotes/ui-services
## [1.31.23](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-26)
**Note:** Version bump only for package @standardnotes/ui-services
## [1.31.22](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-22)
**Note:** Version bump only for package @standardnotes/ui-services
## [1.31.21](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-22)
**Note:** Version bump only for package @standardnotes/ui-services
## [1.31.20](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-22)
**Note:** Version bump only for package @standardnotes/ui-services
## [1.31.19](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-22)
**Note:** Version bump only for package @standardnotes/ui-services
## [1.31.18](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-21)
**Note:** Version bump only for package @standardnotes/ui-services
## [1.31.17](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-20)
**Note:** Version bump only for package @standardnotes/ui-services
## [1.31.16](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-20)
**Note:** Version bump only for package @standardnotes/ui-services
## [1.31.15](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-18)
**Note:** Version bump only for package @standardnotes/ui-services
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/ui-services",
"version": "1.31.15",
"version": "1.31.25",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
@@ -16,7 +16,7 @@
},
"dependencies": {
"@standardnotes/common": "^1.50.0",
"@standardnotes/domain-core": "^1.28.0",
"@standardnotes/domain-core": "^1.34.1",
"@standardnotes/features": "workspace:^",
"@standardnotes/filepicker": "workspace:^",
"@standardnotes/models": "workspace:^",
@@ -222,9 +222,10 @@ export class VaultDisplayService
}
if (this.isInExclusiveDisplayMode()) {
this.exclusivelyShownVault = this.application.vaults.getVault({
const vaultOrError = this.application.vaults.getVault({
keySystemIdentifier: this.options.getExclusivelyShownVault(),
})
this.exclusivelyShownVault = vaultOrError.isFailed() ? undefined : vaultOrError.getValue()
} else {
this.exclusivelyShownVault = undefined
}
+82
View File
@@ -3,6 +3,88 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [3.173.21](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-28)
**Note:** Version bump only for package @standardnotes/web
## [3.173.20](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-26)
**Note:** Version bump only for package @standardnotes/web
## [3.173.19](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-26)
**Note:** Version bump only for package @standardnotes/web
## [3.173.18](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-26)
**Note:** Version bump only for package @standardnotes/web
## [3.173.17](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-25)
**Note:** Version bump only for package @standardnotes/web
## [3.173.16](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-22)
**Note:** Version bump only for package @standardnotes/web
## [3.173.15](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-22)
**Note:** Version bump only for package @standardnotes/web
## [3.173.14](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-22)
**Note:** Version bump only for package @standardnotes/web
## [3.173.13](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-22)
**Note:** Version bump only for package @standardnotes/web
## [3.173.12](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-21)
**Note:** Version bump only for package @standardnotes/web
## [3.173.11](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-21)
**Note:** Version bump only for package @standardnotes/web
## [3.173.10](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-21)
**Note:** Version bump only for package @standardnotes/web
## [3.173.9](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-21)
**Note:** Version bump only for package @standardnotes/web
## [3.173.8](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-20)
### Bug Fixes
* Fixed issue where using table or image option in Super mobile toolbar would make uninteractive ([ec61901](https://github.com/standardnotes/app/commit/ec61901aa5f0c08b7b77029e225e81c19decbd60))
## [3.173.7](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-20)
**Note:** Version bump only for package @standardnotes/web
## [3.173.6](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-20)
**Note:** Version bump only for package @standardnotes/web
## [3.173.5](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-20)
**Note:** Version bump only for package @standardnotes/web
## [3.173.4](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-19)
**Note:** Version bump only for package @standardnotes/web
## [3.173.3](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-19)
**Note:** Version bump only for package @standardnotes/web
## [3.173.2](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-18)
**Note:** Version bump only for package @standardnotes/web
## [3.173.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-18)
**Note:** Version bump only for package @standardnotes/web
+223
View File
@@ -1,5 +1,228 @@
{
"versions": [
{
"version": "3.173.21",
"title": "[3.173.21](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-28)",
"date": null,
"body": "**Note:** Version bump only for package @standardnotes/web",
"parsed": {
"_": [
"Note: Version bump only for package @standardnotes/web"
]
}
},
{
"version": "3.173.20",
"title": "[3.173.20](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-26)",
"date": null,
"body": "**Note:** Version bump only for package @standardnotes/web",
"parsed": {
"_": [
"Note: Version bump only for package @standardnotes/web"
]
}
},
{
"version": "3.173.19",
"title": "[3.173.19](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-26)",
"date": null,
"body": "**Note:** Version bump only for package @standardnotes/web",
"parsed": {
"_": [
"Note: Version bump only for package @standardnotes/web"
]
}
},
{
"version": "3.173.18",
"title": "[3.173.18](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-26)",
"date": null,
"body": "**Note:** Version bump only for package @standardnotes/web",
"parsed": {
"_": [
"Note: Version bump only for package @standardnotes/web"
]
}
},
{
"version": "3.173.17",
"title": "[3.173.17](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-25)",
"date": null,
"body": "**Note:** Version bump only for package @standardnotes/web",
"parsed": {
"_": [
"Note: Version bump only for package @standardnotes/web"
]
}
},
{
"version": "3.173.16",
"title": "[3.173.16](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-22)",
"date": null,
"body": "**Note:** Version bump only for package @standardnotes/web",
"parsed": {
"_": [
"Note: Version bump only for package @standardnotes/web"
]
}
},
{
"version": "3.173.15",
"title": "[3.173.15](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-22)",
"date": null,
"body": "**Note:** Version bump only for package @standardnotes/web",
"parsed": {
"_": [
"Note: Version bump only for package @standardnotes/web"
]
}
},
{
"version": "3.173.14",
"title": "[3.173.14](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-22)",
"date": null,
"body": "**Note:** Version bump only for package @standardnotes/web",
"parsed": {
"_": [
"Note: Version bump only for package @standardnotes/web"
]
}
},
{
"version": "3.173.13",
"title": "[3.173.13](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-22)",
"date": null,
"body": "**Note:** Version bump only for package @standardnotes/web",
"parsed": {
"_": [
"Note: Version bump only for package @standardnotes/web"
]
}
},
{
"version": "3.173.12",
"title": "[3.173.12](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-21)",
"date": null,
"body": "**Note:** Version bump only for package @standardnotes/web",
"parsed": {
"_": [
"Note: Version bump only for package @standardnotes/web"
]
}
},
{
"version": "3.173.11",
"title": "[3.173.11](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-21)",
"date": null,
"body": "**Note:** Version bump only for package @standardnotes/web",
"parsed": {
"_": [
"Note: Version bump only for package @standardnotes/web"
]
}
},
{
"version": "3.173.10",
"title": "[3.173.10](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-21)",
"date": null,
"body": "**Note:** Version bump only for package @standardnotes/web",
"parsed": {
"_": [
"Note: Version bump only for package @standardnotes/web"
]
}
},
{
"version": "3.173.9",
"title": "[3.173.9](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-21)",
"date": null,
"body": "**Note:** Version bump only for package @standardnotes/web",
"parsed": {
"_": [
"Note: Version bump only for package @standardnotes/web"
]
}
},
{
"version": "3.173.8",
"title": "[3.173.8](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-20)",
"date": null,
"body": "### Bug Fixes\n\n* Fixed issue where using table or image option in Super mobile toolbar would make uninteractive ([ec61901](https://github.com/standardnotes/app/commit/ec61901aa5f0c08b7b77029e225e81c19decbd60))",
"parsed": {
"_": [
"Fixed issue where using table or image option in Super mobile toolbar would make uninteractive (ec61901)"
],
"Bug Fixes": [
"Fixed issue where using table or image option in Super mobile toolbar would make uninteractive (ec61901)"
]
}
},
{
"version": "3.173.7",
"title": "[3.173.7](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-20)",
"date": null,
"body": "**Note:** Version bump only for package @standardnotes/web",
"parsed": {
"_": [
"Note: Version bump only for package @standardnotes/web"
]
}
},
{
"version": "3.173.6",
"title": "[3.173.6](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-20)",
"date": null,
"body": "**Note:** Version bump only for package @standardnotes/web",
"parsed": {
"_": [
"Note: Version bump only for package @standardnotes/web"
]
}
},
{
"version": "3.173.5",
"title": "[3.173.5](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-20)",
"date": null,
"body": "**Note:** Version bump only for package @standardnotes/web",
"parsed": {
"_": [
"Note: Version bump only for package @standardnotes/web"
]
}
},
{
"version": "3.173.4",
"title": "[3.173.4](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-19)",
"date": null,
"body": "**Note:** Version bump only for package @standardnotes/web",
"parsed": {
"_": [
"Note: Version bump only for package @standardnotes/web"
]
}
},
{
"version": "3.173.3",
"title": "[3.173.3](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-19)",
"date": null,
"body": "**Note:** Version bump only for package @standardnotes/web",
"parsed": {
"_": [
"Note: Version bump only for package @standardnotes/web"
]
}
},
{
"version": "3.173.2",
"title": "[3.173.2](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-18)",
"date": null,
"body": "**Note:** Version bump only for package @standardnotes/web",
"parsed": {
"_": [
"Note: Version bump only for package @standardnotes/web"
]
}
},
{
"version": "3.173.1",
"title": "[3.173.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-18)",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/web",
"version": "3.173.1",
"version": "3.173.21",
"license": "CC BY-NC-SA 4.0",
"main": "dist/app.js",
"author": "Standard Notes.",
@@ -11,11 +11,35 @@ import Popover from '../Popover/Popover'
import FilePreviewInfoPanel from '../FilePreview/FilePreviewInfoPanel'
import { useFileDragNDrop } from '../FileDragNDropProvider'
import RoundIconButton from '../Button/RoundIconButton'
import { useItemVaultInfo } from '@/Hooks/useItemVaultInfo'
import Icon from '../Icon/Icon'
import { VaultUserServiceEvent } from '@standardnotes/snjs'
const SyncTimeoutNoDebounceMs = 100
const SyncTimeoutDebounceMs = 350
const FileViewWithoutProtection = ({ application, file }: FileViewProps) => {
const { vault } = useItemVaultInfo(file)
const [isReadonly, setIsReadonly] = useState(false)
useEffect(() => {
if (!vault) {
return
}
setIsReadonly(application.vaultUsers.isCurrentUserReadonlyVaultMember(vault))
}, [application.vaultUsers, vault])
useEffect(() => {
return application.vaultUsers.addEventObserver((event, data) => {
if (event === VaultUserServiceEvent.InvalidatedUserCacheForVault) {
if ((data as string) !== vault?.sharing?.sharedVaultUuid) {
return
}
setIsReadonly(vault ? application.vaultUsers.isCurrentUserReadonlyVaultMember(vault) : false)
}
})
}, [application.vaultUsers, vault])
const syncTimeoutRef = useRef<number>()
const fileInfoButtonRef = useRef<HTMLButtonElement>(null)
@@ -67,6 +91,12 @@ const FileViewWithoutProtection = ({ application, file }: FileViewProps) => {
return (
<div className="sn-component section editor" aria-label="File" ref={fileDragTargetRef}>
<div className="flex flex-col">
{isReadonly && (
<div className="bg-warning-faded relative flex items-center px-3.5 py-2 text-sm text-accessory-tint-3">
<Icon type="pencil-off" className="mr-3" />
This file is readonly
</div>
)}
<div
className="content-title-bar section-title-bar section-title-bar z-editor-title-bar w-full"
id="file-title-bar"
@@ -85,11 +115,12 @@ const FileViewWithoutProtection = ({ application, file }: FileViewProps) => {
spellCheck={false}
defaultValue={file.name}
autoComplete="off"
disabled={isReadonly}
/>
</div>
</div>
<div className="flex items-center gap-3">
<LinkedItemsButton linkingController={application.linkingController} />
{!isReadonly && <LinkedItemsButton linkingController={application.linkingController} />}
<RoundIconButton
label="File information panel"
onClick={toggleFileInfoPanel}
@@ -110,7 +141,11 @@ const FileViewWithoutProtection = ({ application, file }: FileViewProps) => {
</div>
</div>
<div className="hidden md:flex">
<LinkedItemBubblesContainer item={file} linkingController={application.linkingController} />
<LinkedItemBubblesContainer
item={file}
linkingController={application.linkingController}
readonly={isReadonly}
/>
</div>
</div>
</div>
@@ -26,6 +26,7 @@ import {
PrefKey,
ProposedSecondsToDeferUILevelSessionExpirationDuringActiveInteraction,
SNNote,
VaultUserServiceEvent,
} from '@standardnotes/snjs'
import { confirmDialog, DELETE_NOTE_KEYBOARD_COMMAND, KeyboardKey } from '@standardnotes/ui-services'
import { ChangeEventHandler, createRef, CSSProperties, KeyboardEventHandler, RefObject } from 'react'
@@ -97,6 +98,7 @@ class NoteView extends AbstractComponent<NoteViewProps, State> {
private removeNoteStreamObserver?: () => void
private removeComponentManagerObserver?: () => void
private removeInnerNoteObserver?: () => void
private removeVaultUsersEventHandler?: () => void
private protectionTimeoutId: ReturnType<typeof setTimeout> | null = null
private noteViewElementRef: RefObject<HTMLDivElement>
@@ -158,6 +160,9 @@ class NoteView extends AbstractComponent<NoteViewProps, State> {
this.removeTrashKeyObserver?.()
this.removeTrashKeyObserver = undefined
this.removeVaultUsersEventHandler?.()
this.removeVaultUsersEventHandler = undefined
this.clearNoteProtectionInactivityTimer()
;(this.ensureNoteIsInsertedBeforeUIAction as unknown) = undefined
@@ -211,6 +216,18 @@ class NoteView extends AbstractComponent<NoteViewProps, State> {
override componentDidMount(): void {
super.componentDidMount()
this.removeVaultUsersEventHandler = this.application.vaultUsers.addEventObserver((event, data) => {
if (event === VaultUserServiceEvent.InvalidatedUserCacheForVault) {
const vault = this.application.vaults.getItemVault(this.note)
if ((data as string) !== vault?.sharing?.sharedVaultUuid) {
return
}
this.setState({
readonly: vault ? this.application.vaultUsers.isCurrentUserReadonlyVaultMember(vault) : false,
})
}
})
this.registerKeyboardShortcuts()
this.removeInnerNoteObserver = this.controller.addNoteInnerValueChangeObserver((note, source) => {
@@ -846,7 +863,7 @@ class NoteView extends AbstractComponent<NoteViewProps, State> {
? 'component'
: 'plain'
const shouldShowConflictsButton = this.state.conflictedNotes.length > 0
const shouldShowConflictsButton = this.state.conflictedNotes.length > 0 && !this.state.readonly
return (
<div aria-label="Note" className="section editor sn-component h-full md:max-h-full" ref={this.noteViewElementRef}>
@@ -230,7 +230,7 @@ const NotesOptions = ({ notes, closeMenu }: NotesOptionsProps) => {
<>
{notes.length === 1 && (
<>
<MenuItem onClick={openRevisionHistoryModal} disabled={areSomeNotesInReadonlySharedVault}>
<MenuItem onClick={openRevisionHistoryModal}>
<Icon type="history" className={iconClass} />
Note history
{historyShortcut && <KeyboardShortcutIndicator className="ml-auto" shortcut={historyShortcut} />}
@@ -22,7 +22,14 @@ const ContactItem = ({ contact }: Props) => {
const deleteContact = useCallback(async () => {
if (await application.alerts.confirm('Are you sure you want to delete this contact?')) {
void application.contacts.deleteContact(contact)
const result = await application.contacts.deleteContact(contact)
if (result.isFailed()) {
application.alerts
.alertV2({
text: result.getError(),
})
.catch(console.error)
}
}
}, [application.alerts, application.contacts, contact])
@@ -130,7 +130,7 @@ const ContactInviteModal: FunctionComponent<Props> = ({ vault, onCloseDialog })
wrapper: 'col-start-2',
}}
items={Object.keys(SharedVaultUserPermission.PERMISSIONS).map((key) => ({
label: application.vaultUsers.getFormattedMemberPermission(key),
label: application.vaultUsers.getFormattedMemberPermission(key.toLowerCase()),
value: key,
}))}
value={selectedContact.permission}
@@ -15,6 +15,9 @@ import {
SharedVaultServiceEvent,
VaultUserServiceEvent,
RoleName,
ProtocolVersion,
compareVersions,
VaultInviteServiceEvent,
} from '@standardnotes/snjs'
import VaultItem from './Vaults/VaultItem'
import Button from '@/Components/Button/Button'
@@ -24,7 +27,7 @@ import PreferencesPane from '../../PreferencesComponents/PreferencesPane'
import { ToastType, addToast } from '@standardnotes/toast'
import NoProSubscription from '../Account/NoProSubscription'
const Vaults = () => {
const Vaults = observer(() => {
const application = useApplication()
const hasAccount = application.hasAccount()
@@ -68,6 +71,13 @@ const Vaults = () => {
const updateInvites = useCallback(async () => {
setInvites(application.vaultInvites.getCachedPendingInviteRecords())
}, [application.vaultInvites])
useEffect(() => {
return application.vaultInvites.addEventObserver((event) => {
if (event === VaultInviteServiceEvent.InvitesReloaded) {
void updateInvites()
}
})
}, [application.vaultInvites, updateInvites])
const updateContacts = useCallback(async () => {
setContacts(contactService.getAllContacts())
@@ -136,13 +146,11 @@ const Vaults = () => {
<ModalOverlay isOpen={isAddContactModalOpen} close={closeAddContactModal}>
<EditContactModal onCloseDialog={closeAddContactModal} />
</ModalOverlay>
<EditVaultModal
isVaultModalOpen={isVaultModalOpen}
creatingSharedVault={isCreatingSharedVault}
closeVaultModal={closeVaultModal}
/>
{invites.length > 0 && (
<PreferencesGroup>
<PreferencesSegment>
@@ -155,7 +163,6 @@ const Vaults = () => {
</PreferencesSegment>
</PreferencesGroup>
)}
{hasAccount && (
<PreferencesGroup>
<PreferencesSegment>
@@ -173,33 +180,6 @@ const Vaults = () => {
</PreferencesSegment>
</PreferencesGroup>
)}
<PreferencesGroup>
<PreferencesSegment>
<Title>Vaults</Title>
{vaults.length > 0 && (
<div className="my-2 flex flex-col gap-3.5">
{vaults.map((vault) => {
return <VaultItem vault={vault} key={vault.uuid} />
})}
</div>
)}
{canCreateMoreVaults ? (
<div className="mt-2.5 flex gap-3">
<Button label="Create Vault" onClick={createNewVault} />
<Button label="Create Shared Vault" onClick={createNewSharedVault} />
</div>
) : (
<div className="mt-3.5">
<NoProSubscription
application={application}
text={<span>Please upgrade in order to increase your shared vault limit.</span>}
/>
</div>
)}
</PreferencesSegment>
</PreferencesGroup>
{hasAccount && (
<PreferencesGroup>
<PreferencesSegment>
@@ -242,8 +222,59 @@ const Vaults = () => {
</PreferencesSegment>
</PreferencesGroup>
)}
<PreferencesGroup>
<PreferencesSegment>
<Title>Vaults</Title>
{vaults.length > 0 && (
<div className="my-2 flex flex-col gap-3.5">
{vaults.map((vault) => {
return <VaultItem vault={vault} key={vault.uuid} />
})}
</div>
)}
{canCreateMoreVaults ? (
<div className="mt-2.5 flex gap-3">
<Button label="Create Vault" onClick={createNewVault} />
{hasAccount && <Button label="Create Shared Vault" onClick={createNewSharedVault} />}
</div>
) : (
<div className="mt-3.5">
<NoProSubscription
application={application}
text={<span>Please upgrade in order to increase your shared vault limit.</span>}
/>
</div>
)}
</PreferencesSegment>
</PreferencesGroup>
</PreferencesPane>
)
})
const VaultsWrapper = () => {
const application = useApplication()
const hasAccount = application.hasAccount()
const accountProtocolVersion = application.getUserVersion()
const isAccountProtocolNotSupported =
accountProtocolVersion && compareVersions(accountProtocolVersion, ProtocolVersion.V004) < 0
if (hasAccount && isAccountProtocolNotSupported) {
return (
<PreferencesPane>
<PreferencesGroup>
<Title>Account update required</Title>
<Subtitle>
In order to use Vaults, you must update your account to use the latest data encryption version.
</Subtitle>
<Button primary className="mt-3" onClick={() => application.upgradeProtocolVersion().catch(console.error)}>
Update Account
</Button>
</PreferencesGroup>
</PreferencesPane>
)
}
return <Vaults />
}
export default observer(Vaults)
export default observer(VaultsWrapper)
@@ -12,6 +12,7 @@ import MobileModalAction from '../Modal/MobileModalAction'
import Popover from '../Popover/Popover'
import MobileModalHeader from '../Modal/MobileModalHeader'
import { useApplication } from '../ApplicationProvider'
import { useItemVaultInfo } from '@/Hooks/useItemVaultInfo'
const HistoryModalDialogContent = ({ dismissModal, note }: RevisionHistoryModalContentProps) => {
const application = useApplication()
@@ -40,6 +41,9 @@ const HistoryModalDialogContent = ({ dismissModal, note }: RevisionHistoryModalC
const [showTabMenu, setShowTabMenu] = useState(false)
const toggleTabMenu = () => setShowTabMenu((show) => !show)
const { vault } = useItemVaultInfo(note)
const isReadonly = vault ? application.vaultUsers.isCurrentUserReadonlyVaultMember(vault) : false
return (
<>
<MobileModalHeader className="items-center border-b border-border px-2 py-1 md:hidden">
@@ -104,7 +108,11 @@ const HistoryModalDialogContent = ({ dismissModal, note }: RevisionHistoryModalC
<HistoryModalContentPane noteHistoryController={noteHistoryController} note={note} />
</div>
</div>
<HistoryModalFooter dismissModal={dismissModal} noteHistoryController={noteHistoryController} />
<HistoryModalFooter
dismissModal={dismissModal}
noteHistoryController={noteHistoryController}
readonly={isReadonly}
/>
</>
)
}
@@ -8,9 +8,10 @@ import Spinner from '@/Components/Spinner/Spinner'
type Props = {
dismissModal: () => void
noteHistoryController: NoteHistoryController
readonly?: boolean
}
const HistoryModalFooter = ({ dismissModal, noteHistoryController }: Props) => {
const HistoryModalFooter = ({ dismissModal, noteHistoryController, readonly = false }: Props) => {
const { selectedRevision, restoreRevision, restoreRevisionAsCopy, selectedEntry, deleteRemoteRevision } =
noteHistoryController
@@ -43,7 +44,7 @@ const HistoryModalFooter = ({ dismissModal, noteHistoryController }: Props) => {
return (
<div className="min-h-6 flex flex-shrink-0 flex-wrap items-center gap-2.5 border-t border-solid border-border px-2.5 py-2 md:justify-between">
<Button className="py-1.35" label="Close" onClick={dismissModal} />
{selectedRevision && selectedEntry && (
{selectedRevision && selectedEntry && !readonly && (
<>
{(selectedEntry as RevisionMetadata).uuid && (
<Button className="md:ml-auto" onClick={deleteSelectedRevision}>
@@ -372,17 +372,17 @@ const MobileToolbarPlugin = () => {
}, [editor, updateEditorSelection])
const isFocusInEditorOrToolbar = isInEditor || isInToolbar || isInLinkEditor
if (!isMobile || !isFocusInEditorOrToolbar) {
return null
}
return (
<>
{modal}
<div className="bg-contrast" id="super-mobile-toolbar">
<div
className={classNames('bg-contrast', !isMobile || !isFocusInEditorOrToolbar ? 'hidden' : '')}
id="super-mobile-toolbar"
>
{isSelectionLink && (
<div
className="border-t border-border px-2 focus:outline-none focus:shadow-none"
className="border-t border-border px-2 focus:shadow-none focus:outline-none"
ref={linkEditorRef}
tabIndex={FOCUSABLE_BUT_NOT_TABBABLE}
>
@@ -406,7 +406,7 @@ const MobileToolbarPlugin = () => {
return (
<StyledTooltip showOnMobile showOnHover label={item.name} key={item.name}>
<button
className="flex items-center justify-center rounded p-0.5 disabled:opacity-50 select-none hover:bg-default"
className="flex select-none items-center justify-center rounded p-0.5 hover:bg-default disabled:opacity-50"
aria-label={item.name}
onMouseDown={(event) => {
event.preventDefault()
@@ -420,7 +420,7 @@ const MobileToolbarPlugin = () => {
>
<div
className={classNames(
'flex items-center justify-center p-2 rounded transition-colors duration-75',
'flex items-center justify-center rounded p-2 transition-colors duration-75',
item.active && 'bg-info text-info-contrast',
)}
>
@@ -22,7 +22,10 @@ const VaultMenu = observer(({ items }: { items: DecryptedItemInterface[] }) => {
}
for (const item of items) {
await application.vaults.moveItemToVault(vault, item)
const result = await application.vaults.moveItemToVault(vault, item)
if (result.isFailed()) {
console.error(result.getError())
}
}
},
[application, items],
@@ -439,11 +439,7 @@ export class ItemListController
}
private shouldLeaveSelectionUnchanged = (activeController: NoteViewController | FileViewController | undefined) => {
const hasMultipleItemsSelected = this.selectedItemsCount >= 2
return (
hasMultipleItemsSelected || (activeController instanceof NoteViewController && activeController.isTemplateNote)
)
return activeController instanceof NoteViewController && activeController.isTemplateNote
}
/**
@@ -15,6 +15,7 @@ import {
InternalFeatureService,
InternalFeature,
PreferenceServiceInterface,
Result,
} from '@standardnotes/snjs'
import { FilesController } from './FilesController'
import { ItemListController } from './ItemList/ItemListController'
@@ -246,7 +247,7 @@ describe('LinkingController', () => {
application.mutator.associateFileWithNote = jest.fn().mockReturnValue({})
const moveToVaultSpy = (application.vaults.moveItemToVault = jest.fn())
const moveToVaultSpy = (application.vaults.moveItemToVault = jest.fn().mockReturnValue(Result.ok()))
const note = createNote('test', {
uuid: 'note',
@@ -213,7 +213,10 @@ export class LinkingController extends AbstractViewController implements Interna
const noteVault = this.vaults.getItemVault(note)
const fileVault = this.vaults.getItemVault(updatedFile)
if (noteVault && !fileVault) {
await this.vaults.moveItemToVault(noteVault, file)
const result = await this.vaults.moveItemToVault(noteVault, file)
if (result.isFailed()) {
console.error(result.getError())
}
}
}
}
@@ -302,7 +305,19 @@ export class LinkingController extends AbstractViewController implements Interna
const activeItem = this.activeItem
if (activeItem) {
await this.addTagToItem(newTag, activeItem)
const itemVault = this.vaults.getItemVault(activeItem)
if (itemVault) {
const movedTagOrError = await this.vaults.moveItemToVault(itemVault, newTag)
if (movedTagOrError.isFailed()) {
throw new Error(`Failed to move tag to item vault: ${movedTagOrError.getError()}`)
}
const movedTag = movedTagOrError.getValue()
await this.addTagToItem(movedTag as SNTag, activeItem)
} else {
await this.addTagToItem(newTag, activeItem)
}
}
return newTag
+12 -12
View File
@@ -4138,7 +4138,7 @@ __metadata:
resolution: "@standardnotes/api@workspace:packages/api"
dependencies:
"@standardnotes/common": ^1.50.0
"@standardnotes/domain-core": ^1.28.0
"@standardnotes/domain-core": ^1.34.1
"@standardnotes/models": "workspace:*"
"@standardnotes/responses": "workspace:*"
"@standardnotes/utils": "workspace:*"
@@ -4288,7 +4288,7 @@ __metadata:
"@babel/core": "*"
"@babel/preset-env": "*"
"@electron/remote": ^2.0.9
"@standardnotes/domain-core": ^1.28.0
"@standardnotes/domain-core": ^1.34.1
"@standardnotes/electron-clear-data": 1.1.1
"@standardnotes/web": "workspace:*"
"@types/fs-extra": ^11.0.1
@@ -4340,12 +4340,12 @@ __metadata:
languageName: node
linkType: hard
"@standardnotes/domain-core@npm:^1.28.0":
version: 1.28.0
resolution: "@standardnotes/domain-core@npm:1.28.0"
"@standardnotes/domain-core@npm:^1.34.1":
version: 1.34.1
resolution: "@standardnotes/domain-core@npm:1.34.1"
dependencies:
uuid: ^9.0.0
checksum: 9ee58eea35ed5d988513c5a24f751b9c3a2be2a324c30684db54ecbd16f0c8e19a80b8e464bde46e86905590085923c7e1abbe1b792a4ef0a19c1171e8f3b6c2
checksum: 29fa0ee7868b08a5255ce737535aa67b9fe8f0562f7d5a308f4be4a63d46821f34b4ea1d4241064e3025968ae45e09eba3dcbd1ff8d510618114541c42a8e6ae
languageName: node
linkType: hard
@@ -4392,7 +4392,7 @@ __metadata:
dependencies:
"@standardnotes/common": ^1.50.0
"@standardnotes/config": 2.4.3
"@standardnotes/domain-core": ^1.28.0
"@standardnotes/domain-core": ^1.34.1
"@standardnotes/models": "workspace:*"
"@standardnotes/responses": "workspace:*"
"@standardnotes/sncrypto-common": "workspace:*"
@@ -4427,7 +4427,7 @@ __metadata:
resolution: "@standardnotes/features@workspace:packages/features"
dependencies:
"@standardnotes/common": ^1.50.0
"@standardnotes/domain-core": ^1.28.0
"@standardnotes/domain-core": ^1.34.1
"@types/jest": ^29.2.3
"@typescript-eslint/eslint-plugin": "*"
eslint: "*"
@@ -4633,7 +4633,7 @@ __metadata:
resolution: "@standardnotes/models@workspace:packages/models"
dependencies:
"@standardnotes/common": ^1.50.0
"@standardnotes/domain-core": ^1.28.0
"@standardnotes/domain-core": ^1.34.1
"@standardnotes/features": "workspace:*"
"@standardnotes/responses": "workspace:*"
"@standardnotes/sncrypto-common": "workspace:^"
@@ -4729,7 +4729,7 @@ __metadata:
dependencies:
"@standardnotes/api": "workspace:^"
"@standardnotes/common": ^1.50.0
"@standardnotes/domain-core": ^1.28.0
"@standardnotes/domain-core": ^1.34.1
"@standardnotes/encryption": "workspace:^"
"@standardnotes/features": "workspace:^"
"@standardnotes/files": "workspace:^"
@@ -4830,7 +4830,7 @@ __metadata:
"@babel/preset-env": "*"
"@standardnotes/api": "workspace:*"
"@standardnotes/common": ^1.50.0
"@standardnotes/domain-core": ^1.28.0
"@standardnotes/domain-core": ^1.34.1
"@standardnotes/domain-events": ^2.122.0
"@standardnotes/encryption": "workspace:*"
"@standardnotes/features": "workspace:*"
@@ -4955,7 +4955,7 @@ __metadata:
resolution: "@standardnotes/ui-services@workspace:packages/ui-services"
dependencies:
"@standardnotes/common": ^1.50.0
"@standardnotes/domain-core": ^1.28.0
"@standardnotes/domain-core": ^1.34.1
"@standardnotes/features": "workspace:^"
"@standardnotes/filepicker": "workspace:^"
"@standardnotes/models": "workspace:^"