mirror of
https://github.com/standardnotes/app
synced 2026-09-13 00:45:26 -04:00
Compare commits
47
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
eb1a066381 | ||
|
|
4724b08c65 | ||
|
|
a0e0567306 | ||
|
|
7e1eb565af | ||
|
|
78b095117c | ||
|
|
12c4ee4728 | ||
|
|
7c61dd2ba2 | ||
|
|
3653ed4be5 | ||
|
|
fff4696fa2 | ||
|
|
78d9ad3c2a | ||
|
|
eb170843e7 | ||
|
|
70c70131bc | ||
|
|
1d97800d3b | ||
|
|
703fd17dce | ||
|
|
852b656927 | ||
|
|
44c97e26b2 | ||
|
|
7b2290e14d | ||
|
|
d2fad22418 | ||
|
|
af43aa05c1 | ||
|
|
79c5da6c5b | ||
|
|
7c8816e229 | ||
|
|
3505808f66 | ||
|
|
d424d7f3f0 | ||
|
|
439caf8e02 | ||
|
|
168880eec6 | ||
|
|
94de5b73db | ||
|
|
4b1140d8f0 | ||
|
|
397d33285c | ||
|
|
981417f952 | ||
|
|
70cc15c6d9 | ||
|
|
9de994bcba | ||
|
|
896bd1eda7 | ||
|
|
487f3e7b57 | ||
|
|
2ce908da29 | ||
|
|
60f96bc201 | ||
|
|
fe0cd96883 | ||
|
|
b2ead8fc84 | ||
|
|
3777a5389e | ||
|
|
192a2eccb8 | ||
|
|
2a27e512fb | ||
|
|
4aa7314eef | ||
|
|
bd803aed0c | ||
|
|
7364d6b0e0 | ||
|
|
9fa52d9039 | ||
|
|
e357a7820c | ||
|
|
ab1699c114 | ||
|
|
c084dcd11e |
@@ -5,7 +5,8 @@ on:
|
||||
branches: [ main ]
|
||||
|
||||
jobs:
|
||||
Build:
|
||||
build:
|
||||
name: Build & Test
|
||||
if: "${{ contains(github.event.head_commit.message, 'chore(release): publish') == false }}"
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
@@ -14,41 +15,43 @@ jobs:
|
||||
with:
|
||||
token: ${{ secrets.CI_PAT_TOKEN }}
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set up Node
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
registry-url: 'https://registry.npmjs.org'
|
||||
node-version-file: '.nvmrc'
|
||||
cache: 'yarn'
|
||||
|
||||
- name: Setup git config
|
||||
run: |
|
||||
git config --global user.name "standardci"
|
||||
git config --global user.email "[email protected]"
|
||||
- name: Import GPG key
|
||||
uses: crazy-max/ghaction-import-gpg@v4
|
||||
with:
|
||||
gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }}
|
||||
passphrase: ${{ secrets.PASSPHRASE }}
|
||||
git_user_signingkey: true
|
||||
git_commit_gpgsign: true
|
||||
|
||||
- name: Install dependencies
|
||||
run: yarn install --immutable
|
||||
|
||||
- name: Build
|
||||
run: yarn build:all
|
||||
|
||||
- name: ESLint
|
||||
run: yarn lint
|
||||
|
||||
- name: Build Android
|
||||
run: yarn android:bundle
|
||||
|
||||
- name: Test
|
||||
run: yarn test
|
||||
|
||||
build-docker:
|
||||
name: Build Docker Image
|
||||
if: "${{ contains(github.event.head_commit.message, 'chore(release): publish') == false }}"
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
token: ${{ secrets.CI_PAT_TOKEN }}
|
||||
fetch-depth: 0
|
||||
- name: Set up Node
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
registry-url: 'https://registry.npmjs.org'
|
||||
node-version-file: '.nvmrc'
|
||||
cache: 'yarn'
|
||||
- name: Install dependencies
|
||||
run: yarn install --immutable
|
||||
- name: Build
|
||||
run: yarn build:snjs
|
||||
- name: Login to Docker Hub
|
||||
if: "${{ contains(github.event.head_commit.message, 'skip e2e') == false }}"
|
||||
uses: docker/login-action@v2
|
||||
@@ -62,7 +65,12 @@ jobs:
|
||||
yarn docker build @standardnotes/snjs -t standardnotes/snjs:${{ github.sha }}
|
||||
docker push standardnotes/snjs:${{ github.sha }}
|
||||
|
||||
- name: Run E2E test suite
|
||||
e2e-base:
|
||||
name: E2E Base Test Suite
|
||||
needs: build-docker
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Run E2E base test suite
|
||||
if: "${{ contains(github.event.head_commit.message, 'skip e2e') == false }}"
|
||||
uses: convictional/trigger-workflow-and-wait@master
|
||||
with:
|
||||
@@ -71,21 +79,82 @@ jobs:
|
||||
github_token: ${{ secrets.CI_PAT_TOKEN }}
|
||||
workflow_file_name: e2e-test-suite.yml
|
||||
wait_interval: 30
|
||||
client_payload: '{"snjs_image_tag": "${{ github.sha }}", "author": "${{ github.actor }}", "ref_name": "app:${{ github.ref }}"}'
|
||||
client_payload: '{"snjs_image_tag": "${{ github.sha }}", "suite": "base", "author": "${{ github.actor }}", "ref_name": "app:${{ github.ref }}"}'
|
||||
propagate_failure: true
|
||||
trigger_workflow: true
|
||||
wait_workflow: true
|
||||
|
||||
# e2e-vaults:
|
||||
# name: E2E Vaults Test Suite
|
||||
# needs: build-docker
|
||||
# runs-on: ubuntu-latest
|
||||
# steps:
|
||||
# - name: Run E2E vaults test suite
|
||||
# if: "${{ contains(github.event.head_commit.message, 'skip e2e') == false }}"
|
||||
# uses: convictional/trigger-workflow-and-wait@master
|
||||
# with:
|
||||
# owner: standardnotes
|
||||
# repo: server
|
||||
# github_token: ${{ secrets.CI_PAT_TOKEN }}
|
||||
# workflow_file_name: e2e-test-suite.yml
|
||||
# wait_interval: 30
|
||||
# client_payload: '{"snjs_image_tag": "${{ github.sha }}", "suite": "vaults", "author": "${{ github.actor }}", "ref_name": "app:${{ github.ref }}"}'
|
||||
# propagate_failure: true
|
||||
# trigger_workflow: true
|
||||
# wait_workflow: true
|
||||
|
||||
publish:
|
||||
name: Publish to NPM
|
||||
needs: [e2e-base]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
token: ${{ secrets.CI_PAT_TOKEN }}
|
||||
fetch-depth: 0
|
||||
- name: Set up Node
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
registry-url: 'https://registry.npmjs.org'
|
||||
node-version-file: '.nvmrc'
|
||||
cache: 'yarn'
|
||||
- name: Setup git config
|
||||
run: |
|
||||
git config --global user.name "standardci"
|
||||
git config --global user.email "[email protected]"
|
||||
- name: Import GPG key
|
||||
uses: crazy-max/ghaction-import-gpg@v4
|
||||
with:
|
||||
gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }}
|
||||
passphrase: ${{ secrets.PASSPHRASE }}
|
||||
git_user_signingkey: true
|
||||
git_commit_gpgsign: true
|
||||
- name: Install dependencies
|
||||
run: yarn install --immutable
|
||||
- name: Build
|
||||
run: yarn build:all
|
||||
- name: Bump version
|
||||
run: yarn release:prod
|
||||
|
||||
- name: Publish
|
||||
run: yarn publish:prod
|
||||
env:
|
||||
NODE_AUTH_TOKEN: ${{ secrets.CI_NPM_TOKEN }}
|
||||
|
||||
publish-docker:
|
||||
name: Publish to Docker Hub
|
||||
needs: [e2e-base]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Login to Docker Hub
|
||||
if: "${{ contains(github.event.head_commit.message, 'skip e2e') == false }}"
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||
- name: Publish SNJS Docker image as stable
|
||||
if: "${{ contains(github.event.head_commit.message, 'skip e2e') == false }}"
|
||||
run: |
|
||||
docker pull standardnotes/snjs:${{ github.sha }}
|
||||
docker tag standardnotes/snjs:${{ github.sha }} standardnotes/snjs:latest
|
||||
docker push standardnotes/snjs:latest
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
name: SNJS PR — E2E Tests
|
||||
name: SNJS PR — E2E Tests
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
@@ -25,6 +25,38 @@ jobs:
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
token: ${{ secrets.CI_PAT_TOKEN }}
|
||||
fetch-depth: 0
|
||||
- name: Set up Node
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
registry-url: 'https://registry.npmjs.org'
|
||||
node-version-file: '.nvmrc'
|
||||
cache: 'yarn'
|
||||
- name: Install dependencies
|
||||
run: yarn install --immutable
|
||||
- name: Build
|
||||
run: yarn build:snjs
|
||||
- name: ESLint
|
||||
run: yarn lint
|
||||
- name: Test
|
||||
run: yarn test
|
||||
|
||||
build-docker:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
token: ${{ secrets.CI_PAT_TOKEN }}
|
||||
fetch-depth: 0
|
||||
- name: Set up Node
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
registry-url: 'https://registry.npmjs.org'
|
||||
node-version-file: '.nvmrc'
|
||||
cache: 'yarn'
|
||||
- name: Install dependencies
|
||||
run: yarn install --immutable
|
||||
- name: Build
|
||||
@@ -40,6 +72,12 @@ jobs:
|
||||
docker push standardnotes/snjs:${{ github.sha }}
|
||||
docker tag standardnotes/snjs:${{ github.sha }} standardnotes/snjs:test
|
||||
docker push standardnotes/snjs:test
|
||||
|
||||
e2e-base:
|
||||
name: E2E Base Test Suite
|
||||
needs: build-docker
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Run E2E test suite
|
||||
uses: convictional/trigger-workflow-and-wait@master
|
||||
with:
|
||||
@@ -48,7 +86,25 @@ jobs:
|
||||
github_token: ${{ secrets.CI_PAT_TOKEN }}
|
||||
workflow_file_name: e2e-test-suite.yml
|
||||
wait_interval: 30
|
||||
client_payload: '{"snjs_image_tag": "${{ github.sha }}", "author": "${{ github.actor }}", "ref_name": "app:${{ github.ref }}"}'
|
||||
client_payload: '{"snjs_image_tag": "${{ github.sha }}", "suite": "base", "author": "${{ github.actor }}", "ref_name": "app:${{ github.ref }}"}'
|
||||
propagate_failure: true
|
||||
trigger_workflow: true
|
||||
wait_workflow: true
|
||||
|
||||
# e2e-vaults:
|
||||
# name: E2E Vaults Test Suite
|
||||
# needs: build-docker
|
||||
# runs-on: ubuntu-latest
|
||||
# steps:
|
||||
# - name: Run E2E vaults test suite
|
||||
# uses: convictional/trigger-workflow-and-wait@master
|
||||
# with:
|
||||
# owner: standardnotes
|
||||
# repo: server
|
||||
# github_token: ${{ secrets.CI_PAT_TOKEN }}
|
||||
# workflow_file_name: e2e-test-suite.yml
|
||||
# wait_interval: 30
|
||||
# client_payload: '{"snjs_image_tag": "${{ github.sha }}", "suite": "vaults", "author": "${{ github.actor }}", "ref_name": "app:${{ github.ref }}"}'
|
||||
# propagate_failure: true
|
||||
# trigger_workflow: true
|
||||
# wait_workflow: true
|
||||
|
||||
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -3,6 +3,22 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.26.63](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-10-05)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/api
|
||||
|
||||
## [1.26.62](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-29)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/api
|
||||
|
||||
## [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,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/api",
|
||||
"version": "1.26.59",
|
||||
"version": "1.26.63",
|
||||
"engines": {
|
||||
"node": ">=16.0.0 <17.0.0"
|
||||
},
|
||||
@@ -36,7 +36,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@standardnotes/common": "^1.50.0",
|
||||
"@standardnotes/domain-core": "^1.32.0",
|
||||
"@standardnotes/domain-core": "^1.34.1",
|
||||
"@standardnotes/models": "workspace:*",
|
||||
"@standardnotes/responses": "workspace:*",
|
||||
"@standardnotes/utils": "workspace:*",
|
||||
|
||||
@@ -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>>
|
||||
|
||||
@@ -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.1.250](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-10-05)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/clipper
|
||||
|
||||
## [1.1.249](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-10-02)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/clipper
|
||||
|
||||
## [1.1.248](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-29)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/clipper
|
||||
|
||||
## [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,7 +1,7 @@
|
||||
{
|
||||
"name": "@standardnotes/clipper",
|
||||
"description": "Web clipper browser extension for Standard Notes",
|
||||
"version": "1.1.240",
|
||||
"version": "1.1.250",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"build-mv2": "yarn clean && webpack --config ./webpack.config.prod.js",
|
||||
|
||||
@@ -3,6 +3,50 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [3.108.183](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-10-05)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/desktop
|
||||
|
||||
## [3.108.182](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-10-02)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/desktop
|
||||
|
||||
## [3.108.181](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-29)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/desktop
|
||||
|
||||
## [3.108.180](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-29)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/desktop
|
||||
|
||||
## [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
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@standardnotes/desktop",
|
||||
"main": "./app/dist/index.js",
|
||||
"version": "3.108.172",
|
||||
"version": "3.108.183",
|
||||
"license": "CC BY-NC-SA 4.0",
|
||||
"author": "Standard Notes.",
|
||||
"private": true,
|
||||
@@ -35,14 +35,14 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@electron/remote": "^2.0.9",
|
||||
"@standardnotes/domain-core": "^1.32.0",
|
||||
"@standardnotes/domain-core": "^1.34.1",
|
||||
"@standardnotes/electron-clear-data": "1.1.1",
|
||||
"@standardnotes/web": "workspace:*",
|
||||
"axios": "^1.1.3",
|
||||
"compare-versions": "^5.0.1",
|
||||
"decrypt": "github:standardnotes/decrypt#master",
|
||||
"dotenv": "^16.0.3",
|
||||
"electron": "22.1.0",
|
||||
"electron": "22.3.24",
|
||||
"electron-log": "^4.4.8",
|
||||
"electron-updater": "^5.3.0",
|
||||
"fs-extra": "^11.1.1",
|
||||
|
||||
@@ -3,6 +3,22 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.21.85](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-10-05)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/encryption
|
||||
|
||||
## [1.21.84](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-29)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/encryption
|
||||
|
||||
## [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,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/encryption",
|
||||
"version": "1.21.81",
|
||||
"version": "1.21.85",
|
||||
"engines": {
|
||||
"node": ">=16.0.0 <17.0.0"
|
||||
},
|
||||
@@ -29,7 +29,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@standardnotes/common": "^1.50.0",
|
||||
"@standardnotes/domain-core": "^1.32.0",
|
||||
"@standardnotes/domain-core": "^1.34.1",
|
||||
"@standardnotes/models": "workspace:*",
|
||||
"@standardnotes/responses": "workspace:*",
|
||||
"@standardnotes/sncrypto-common": "workspace:*",
|
||||
|
||||
@@ -3,6 +3,20 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [1.60.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-10-05)
|
||||
|
||||
### Features
|
||||
|
||||
* Added "Private vaults" as a Labs feature. Can be enabled from Preferences > General > Labs ([#2562](https://github.com/standardnotes/app/issues/2562)) ([4724b08](https://github.com/standardnotes/app/commit/4724b08c65c48b6b7232885de205a44b080313b1))
|
||||
|
||||
## [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,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/features",
|
||||
"version": "1.59.19",
|
||||
"version": "1.60.0",
|
||||
"engines": {
|
||||
"node": ">=16.0.0 <17.0.0"
|
||||
},
|
||||
@@ -26,7 +26,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@standardnotes/common": "^1.50.0",
|
||||
"@standardnotes/domain-core": "^1.32.0",
|
||||
"@standardnotes/domain-core": "^1.34.1",
|
||||
"reflect-metadata": "^0.1.13"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -41,6 +41,7 @@ export class NativeFeatureIdentifier extends ValueObject<NativeFeatureIdentifier
|
||||
|
||||
Clipper: 'org.standardnotes.clipper',
|
||||
|
||||
Vaults: 'org.standardnotes.vaults',
|
||||
SharedVaults: 'org.standardnotes.shared-vaults',
|
||||
|
||||
DeprecatedMarkdownVisualEditor: 'org.standardnotes.markdown-visual-editor',
|
||||
@@ -74,4 +75,4 @@ export class NativeFeatureIdentifier extends ValueObject<NativeFeatureIdentifier
|
||||
/**
|
||||
* Identifier for standalone filesafe instance offered as legacy installable via extensions-server
|
||||
*/
|
||||
export const ExperimentalFeatures = []
|
||||
export const ExperimentalFeatures = [NativeFeatureIdentifier.TYPES.Vaults]
|
||||
|
||||
@@ -1,5 +1,16 @@
|
||||
import { RoleName } from '@standardnotes/domain-core'
|
||||
import { AnyFeatureDescription } from '../Feature/AnyFeatureDescription'
|
||||
import { NativeFeatureIdentifier } from '../Feature/NativeFeatureIdentifier'
|
||||
import { PermissionName } from '../Permission/PermissionName'
|
||||
|
||||
export function experimentalFeatures(): AnyFeatureDescription[] {
|
||||
return []
|
||||
return [
|
||||
{
|
||||
name: 'Private vaults',
|
||||
description: 'Private vaults allow you to store notes, files and tags into separate, encrypted vaults.',
|
||||
availableInRoles: [RoleName.NAMES.CoreUser, RoleName.NAMES.PlusUser, RoleName.NAMES.ProUser],
|
||||
identifier: NativeFeatureIdentifier.TYPES.Vaults,
|
||||
permission_name: PermissionName.Vaults,
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
@@ -38,5 +38,6 @@ export enum PermissionName {
|
||||
SubscriptionSharing = 'server:subscription-sharing',
|
||||
SuperEditor = 'editor:super-editor',
|
||||
Clipper = 'app:clipper',
|
||||
Vaults = 'app:vaults',
|
||||
SharedVaults = 'server:shared-vaults',
|
||||
}
|
||||
|
||||
@@ -3,6 +3,22 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.28.96](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-10-05)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/filepicker
|
||||
|
||||
## [1.28.95](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-29)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/filepicker
|
||||
|
||||
## [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,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/filepicker",
|
||||
"version": "1.28.92",
|
||||
"version": "1.28.96",
|
||||
"engines": {
|
||||
"node": ">=16.0.0 <17.0.0"
|
||||
},
|
||||
|
||||
@@ -3,6 +3,22 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.17.15](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-10-05)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/files
|
||||
|
||||
## [1.17.14](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-29)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/files
|
||||
|
||||
## [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,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/files",
|
||||
"version": "1.17.11",
|
||||
"version": "1.17.15",
|
||||
"engines": {
|
||||
"node": ">=16.0.0 <17.0.0"
|
||||
},
|
||||
|
||||
@@ -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.
|
||||
|
||||
## [3.56.161](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-10-05)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/mobile
|
||||
|
||||
## [3.56.160](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-10-02)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/mobile
|
||||
|
||||
## [3.56.159](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-29)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/mobile
|
||||
|
||||
## [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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/mobile",
|
||||
"version": "3.56.151",
|
||||
"version": "3.56.161",
|
||||
"author": "Standard Notes.",
|
||||
"private": true,
|
||||
"license": "CC BY-NC-SA 4.0",
|
||||
|
||||
@@ -3,6 +3,22 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.47.21](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-10-05)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/models
|
||||
|
||||
## [1.47.20](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-29)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/models
|
||||
|
||||
## [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,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/models",
|
||||
"version": "1.47.17",
|
||||
"version": "1.47.21",
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
@@ -20,7 +20,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@standardnotes/common": "^1.50.0",
|
||||
"@standardnotes/domain-core": "^1.32.0",
|
||||
"@standardnotes/domain-core": "^1.34.1",
|
||||
"@standardnotes/features": "workspace:*",
|
||||
"@standardnotes/responses": "workspace:*",
|
||||
"@standardnotes/sncrypto-common": "workspace:^",
|
||||
|
||||
@@ -3,6 +3,50 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.4.535](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-10-05)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/releases
|
||||
|
||||
## [1.4.534](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-10-02)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/releases
|
||||
|
||||
## [1.4.533](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-29)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/releases
|
||||
|
||||
## [1.4.532](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-29)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/releases
|
||||
|
||||
## [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,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/releases",
|
||||
"version": "1.4.524",
|
||||
"version": "1.4.535",
|
||||
"license": "CC BY-NC-SA 4.0",
|
||||
"main": "dist/releases.json",
|
||||
"types": "dist/index.d.ts",
|
||||
|
||||
@@ -3,6 +3,22 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.13.47](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-10-05)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/responses
|
||||
|
||||
## [1.13.46](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-29)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/responses
|
||||
|
||||
## [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,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/responses",
|
||||
"version": "1.13.43",
|
||||
"version": "1.13.47",
|
||||
"engines": {
|
||||
"node": ">=16.0.0 <17.0.0"
|
||||
},
|
||||
|
||||
@@ -4,4 +4,5 @@ export interface SharedVaultUserServerHash {
|
||||
user_uuid: string
|
||||
permission: string
|
||||
updated_at_timestamp: number
|
||||
is_designated_survivor: boolean
|
||||
}
|
||||
|
||||
@@ -3,6 +3,38 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.64.33](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-10-05)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/services
|
||||
|
||||
## [1.64.32](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-10-02)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/services
|
||||
|
||||
## [1.64.31](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-29)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/services
|
||||
|
||||
## [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,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/services",
|
||||
"version": "1.64.25",
|
||||
"version": "1.64.33",
|
||||
"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.32.0",
|
||||
"@standardnotes/domain-core": "^1.34.1",
|
||||
"@standardnotes/encryption": "workspace:^",
|
||||
"@standardnotes/features": "workspace:^",
|
||||
"@standardnotes/files": "workspace:^",
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -96,7 +96,7 @@ 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
|
||||
}
|
||||
}
|
||||
@@ -105,7 +105,7 @@ export class SharedVaultService
|
||||
switch (event.eventPayload.props.type.value) {
|
||||
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())
|
||||
@@ -113,25 +113,38 @@ export class SharedVaultService
|
||||
break
|
||||
}
|
||||
case NotificationType.TYPES.UserRemovedFromSharedVault: {
|
||||
this.vaultUsers.invalidateVaultUsersCache(event.eventPayload.props.sharedVaultUuid.value).catch(console.error)
|
||||
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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
+66
@@ -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()
|
||||
}
|
||||
}
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
export interface DesignateSurvivorDTO {
|
||||
sharedVaultUuid: string
|
||||
sharedVaultMemberUuid: string
|
||||
}
|
||||
@@ -14,6 +14,7 @@ import { Result } from '@standardnotes/domain-core'
|
||||
import { IsVaultOwner } from './UseCase/IsVaultOwner'
|
||||
import { IsReadonlyVaultMember } from './UseCase/IsReadonlyVaultMember'
|
||||
import { IsVaultAdmin } from './UseCase/IsVaultAdmin'
|
||||
import { DesignateSurvivor } from './UseCase/DesignateSurvivor/DesignateSurvivor'
|
||||
|
||||
export class VaultUserService extends AbstractService<VaultUserServiceEvent> implements VaultUserServiceInterface {
|
||||
constructor(
|
||||
@@ -26,6 +27,7 @@ export class VaultUserService extends AbstractService<VaultUserServiceEvent> imp
|
||||
private _isReadonlyVaultMember: IsReadonlyVaultMember,
|
||||
private _getVault: GetVault,
|
||||
private _leaveVault: LeaveVault,
|
||||
private designateSurvivorUseCase: DesignateSurvivor,
|
||||
eventBus: InternalEventBusInterface,
|
||||
) {
|
||||
super(eventBus)
|
||||
@@ -41,6 +43,19 @@ export class VaultUserService extends AbstractService<VaultUserServiceEvent> imp
|
||||
;(this._leaveVault as unknown) = undefined
|
||||
}
|
||||
|
||||
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({
|
||||
|
||||
@@ -12,6 +12,7 @@ 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
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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.
|
||||
|
||||
## [2.202.95](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-10-05)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/snjs
|
||||
|
||||
## [2.202.94](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-10-02)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/snjs
|
||||
|
||||
## [2.202.93](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-29)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/snjs
|
||||
|
||||
## [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
|
||||
|
||||
@@ -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),
|
||||
)
|
||||
})
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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) {
|
||||
@@ -880,7 +892,7 @@ export class ItemManager extends Services.AbstractService implements Services.It
|
||||
? this.navigationDisplayController
|
||||
.items()
|
||||
.filter((item) => Models.isNote(item) && this.collection.uuidsOfItemsWithConflicts().includes(item.uuid))
|
||||
: this.findItems(uuids)
|
||||
: this.findItems(uuids).filter(Models.isNote)
|
||||
return items.length
|
||||
}
|
||||
|
||||
|
||||
@@ -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',
|
||||
],
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import { createNoteParams } from './lib/Items.js'
|
||||
chai.use(chaiAsPromised)
|
||||
const expect = chai.expect
|
||||
|
||||
describe.skip('history manager', () => {
|
||||
describe('history manager', () => {
|
||||
const largeCharacterChange = 25
|
||||
|
||||
const syncOptions = {
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -44,9 +44,11 @@ export class VaultsContext extends AppContext {
|
||||
await this.awaitPromiseOrDoNothing(
|
||||
promise,
|
||||
1,
|
||||
'Waiting for notifications timed out. Notifications might have been processed in previous sync.'
|
||||
'Waiting for notifications timed out. Notifications might have been processed in previous sync.',
|
||||
)
|
||||
|
||||
await this.sync()
|
||||
|
||||
if (this.notifications['handleReceivedNotifications'].restore) {
|
||||
this.notifications['handleReceivedNotifications'].restore()
|
||||
}
|
||||
@@ -112,7 +114,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()
|
||||
})
|
||||
})
|
||||
|
||||
@@ -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')
|
||||
|
||||
|
||||
@@ -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()
|
||||
})
|
||||
})
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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')
|
||||
|
||||
@@ -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)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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,352 @@
|
||||
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.skip('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 () {
|
||||
if (context && !context.application.dealloced) {
|
||||
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('owner of a shared vault with a designated survivor removing the vault', () => {
|
||||
it('should 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()
|
||||
|
||||
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 }).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: sharedVault.systemIdentifier }).isFailed()).to.be.true
|
||||
expect(thirdContext.keys.getPrimaryKeySystemRootKey(sharedVault.systemIdentifier)).to.be.undefined
|
||||
expect(thirdContext.keys.getKeySystemItemsKeys(sharedVault.systemIdentifier)).to.be.empty
|
||||
})
|
||||
|
||||
it('should not 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 contactNote = secondContext.items.findItem(note.uuid)
|
||||
expect(contactNote).to.be.undefined
|
||||
|
||||
const thirdPartyNote = thirdContext.items.findItem(note.uuid)
|
||||
expect(thirdPartyNote).to.be.undefined
|
||||
})
|
||||
|
||||
it('should not 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.be.undefined
|
||||
})
|
||||
|
||||
it('should not 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 contactVaultOrError = secondContext.vaults.getVault({ keySystemIdentifier: sharedVault.systemIdentifier })
|
||||
expect(contactVaultOrError.isFailed()).to.be.true
|
||||
|
||||
const thirdPartyVaultOrError = thirdContext.vaults.getVault({ keySystemIdentifier: sharedVault.systemIdentifier })
|
||||
expect(thirdPartyVaultOrError.isFailed()).to.be.true
|
||||
})
|
||||
})
|
||||
|
||||
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()
|
||||
await context.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
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/snjs",
|
||||
"version": "2.202.85",
|
||||
"version": "2.202.95",
|
||||
"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.32.0",
|
||||
"@standardnotes/domain-core": "^1.34.1",
|
||||
"@standardnotes/domain-events": "^2.122.0",
|
||||
"@standardnotes/encryption": "workspace:*",
|
||||
"@standardnotes/features": "workspace:*",
|
||||
|
||||
@@ -3,6 +3,38 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.31.28](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-10-05)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/ui-services
|
||||
|
||||
## [1.31.27](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-10-02)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/ui-services
|
||||
|
||||
## [1.31.26](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-29)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/ui-services
|
||||
|
||||
## [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,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/ui-services",
|
||||
"version": "1.31.20",
|
||||
"version": "1.31.28",
|
||||
"engines": {
|
||||
"node": ">=16.0.0 <17.0.0"
|
||||
},
|
||||
@@ -16,7 +16,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@standardnotes/common": "^1.50.0",
|
||||
"@standardnotes/domain-core": "^1.32.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
|
||||
}
|
||||
|
||||
@@ -3,6 +3,50 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [3.174.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-10-05)
|
||||
|
||||
### Features
|
||||
|
||||
* Added "Private vaults" as a Labs feature. Can be enabled from Preferences > General > Labs ([#2562](https://github.com/standardnotes/app/issues/2562)) ([4724b08](https://github.com/standardnotes/app/commit/4724b08c65c48b6b7232885de205a44b080313b1))
|
||||
|
||||
## [3.173.23](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-10-02)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Fixed issue where super note would be editable even when "Prevent editing" is enabled ([d2fad22](https://github.com/standardnotes/app/commit/d2fad2241846733dceed4af6094999893d77584d))
|
||||
|
||||
## [3.173.22](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-29)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/web
|
||||
|
||||
## [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
|
||||
|
||||
@@ -1,5 +1,121 @@
|
||||
{
|
||||
"versions": [
|
||||
{
|
||||
"version": "3.174.0",
|
||||
"title": "[3.174.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-10-05)",
|
||||
"date": null,
|
||||
"body": "### Features\n\n* Added \"Private vaults\" as a Labs feature. Can be enabled from Preferences > General > Labs ([#2562](https://github.com/standardnotes/app/issues/2562)) ([4724b08](https://github.com/standardnotes/app/commit/4724b08c65c48b6b7232885de205a44b080313b1))",
|
||||
"parsed": {
|
||||
"_": [
|
||||
"Added \"Private vaults\" as a Labs feature. Can be enabled from Preferences > General > Labs (#2562) (4724b08)"
|
||||
],
|
||||
"Features": [
|
||||
"Added \"Private vaults\" as a Labs feature. Can be enabled from Preferences > General > Labs (#2562) (4724b08)"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"version": "3.173.23",
|
||||
"title": "[3.173.23](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-10-02)",
|
||||
"date": null,
|
||||
"body": "### Bug Fixes\n\n* Fixed issue where super note would be editable even when \"Prevent editing\" is enabled ([d2fad22](https://github.com/standardnotes/app/commit/d2fad2241846733dceed4af6094999893d77584d))",
|
||||
"parsed": {
|
||||
"_": [
|
||||
"Fixed issue where super note would be editable even when \"Prevent editing\" is enabled (d2fad22)"
|
||||
],
|
||||
"Bug Fixes": [
|
||||
"Fixed issue where super note would be editable even when \"Prevent editing\" is enabled (d2fad22)"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"version": "3.173.22",
|
||||
"title": "[3.173.22](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-29)",
|
||||
"date": null,
|
||||
"body": "**Note:** Version bump only for package @standardnotes/web",
|
||||
"parsed": {
|
||||
"_": [
|
||||
"Note: Version bump only for package @standardnotes/web"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"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)",
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
{
|
||||
"name": "@standardnotes/web",
|
||||
"version": "3.173.14",
|
||||
"version": "3.174.0",
|
||||
"license": "CC BY-NC-SA 4.0",
|
||||
"main": "dist/app.js",
|
||||
"author": "Standard Notes.",
|
||||
"author": "Standard Notes",
|
||||
"private": true,
|
||||
"files": [
|
||||
"dist"
|
||||
|
||||
@@ -102,7 +102,7 @@ const FileMenuOptions: FunctionComponent<Props> = ({
|
||||
) : null}
|
||||
</>
|
||||
)}
|
||||
{application.featuresController.isEntitledToVaults() && (
|
||||
{application.featuresController.isVaultsEnabled() && (
|
||||
<AddToVaultMenuOption
|
||||
iconClassName={iconClass}
|
||||
items={selectedFiles}
|
||||
|
||||
@@ -16,7 +16,7 @@ const VaultSelectionButton = ({ isMobileNavigation = false }: { isMobileNavigati
|
||||
const [isOpen, setIsOpen] = useState(false)
|
||||
const toggleMenu = () => setIsOpen(!isOpen)
|
||||
|
||||
if (!application.featuresController.isEntitledToVaults()) {
|
||||
if (!application.featuresController.isVaultsEnabled()) {
|
||||
return null
|
||||
}
|
||||
|
||||
|
||||
@@ -69,7 +69,7 @@ type State = {
|
||||
isDesktop?: boolean
|
||||
editorLineWidth: EditorLineWidth
|
||||
noteLocked: boolean
|
||||
readonly: boolean
|
||||
readonly?: boolean
|
||||
noteStatus?: NoteStatus
|
||||
saveError?: boolean
|
||||
showProtectedWarning: boolean
|
||||
@@ -129,7 +129,7 @@ class NoteView extends AbstractComponent<NoteViewProps, State> {
|
||||
isDesktop: isDesktopApplication(),
|
||||
noteStatus: undefined,
|
||||
noteLocked: this.controller.item.locked,
|
||||
readonly: itemVault ? this.application.vaultUsers.isCurrentUserReadonlyVaultMember(itemVault) : false,
|
||||
readonly: itemVault ? this.application.vaultUsers.isCurrentUserReadonlyVaultMember(itemVault) : undefined,
|
||||
showProtectedWarning: false,
|
||||
spellcheck: true,
|
||||
stackComponentViewers: [],
|
||||
@@ -223,7 +223,7 @@ class NoteView extends AbstractComponent<NoteViewProps, State> {
|
||||
return
|
||||
}
|
||||
this.setState({
|
||||
readonly: vault ? this.application.vaultUsers.isCurrentUserReadonlyVaultMember(vault) : false,
|
||||
readonly: vault ? this.application.vaultUsers.isCurrentUserReadonlyVaultMember(vault) : undefined,
|
||||
})
|
||||
}
|
||||
})
|
||||
@@ -1008,7 +1008,7 @@ class NoteView extends AbstractComponent<NoteViewProps, State> {
|
||||
spellcheck={this.state.spellcheck}
|
||||
ref={this.setPlainEditorRef}
|
||||
controller={this.controller}
|
||||
locked={this.state.noteLocked || this.state.readonly}
|
||||
locked={this.state.noteLocked || !!this.state.readonly}
|
||||
onFocus={this.onPlainFocus}
|
||||
onBlur={this.onPlainBlur}
|
||||
/>
|
||||
|
||||
@@ -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} />}
|
||||
@@ -286,7 +286,7 @@ const NotesOptions = ({ notes, closeMenu }: NotesOptionsProps) => {
|
||||
)}
|
||||
<HorizontalSeparator classes="my-2" />
|
||||
|
||||
{application.featuresController.isEntitledToVaults() && (
|
||||
{application.featuresController.isVaultsEnabled() && (
|
||||
<AddToVaultMenuOption iconClassName={iconClass} items={notes} disabled={!hasAdminPermissionForAllSharedNotes} />
|
||||
)}
|
||||
|
||||
|
||||
@@ -49,7 +49,7 @@ class PasswordWizard extends AbstractComponent<Props, State> {
|
||||
continueTitle: DEFAULT_CONTINUE_TITLE,
|
||||
}
|
||||
|
||||
if (props.application.featuresController.isEntitledToVaults()) {
|
||||
if (props.application.featuresController.isVaultsEnabled()) {
|
||||
this.state = {
|
||||
...baseState,
|
||||
lockContinue: true,
|
||||
|
||||
+1
-1
@@ -26,7 +26,7 @@ export class PreferencesSessionController {
|
||||
? PREFERENCES_MENU_ITEMS.slice()
|
||||
: READY_PREFERENCES_MENU_ITEMS.slice()
|
||||
|
||||
if (application.featuresController.isEntitledToVaults()) {
|
||||
if (application.featuresController.isVaultsEnabled()) {
|
||||
menuItems.push({ id: 'vaults', label: 'Vaults', icon: 'safe-square', order: 5 })
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ import {
|
||||
RoleName,
|
||||
ProtocolVersion,
|
||||
compareVersions,
|
||||
VaultInviteServiceEvent,
|
||||
} from '@standardnotes/snjs'
|
||||
import VaultItem from './Vaults/VaultItem'
|
||||
import Button from '@/Components/Button/Button'
|
||||
@@ -30,6 +31,7 @@ const Vaults = observer(() => {
|
||||
const application = useApplication()
|
||||
|
||||
const hasAccount = application.hasAccount()
|
||||
const isSharedVaultsEnabled = application.featuresController.isEntitledToSharedVaults()
|
||||
|
||||
const [vaults, setVaults] = useState<VaultListingInterface[]>([])
|
||||
const [canCreateMoreVaults, setCanCreateMoreVaults] = useState(true)
|
||||
@@ -70,6 +72,13 @@ const Vaults = observer(() => {
|
||||
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())
|
||||
@@ -155,7 +164,7 @@ const Vaults = observer(() => {
|
||||
</PreferencesSegment>
|
||||
</PreferencesGroup>
|
||||
)}
|
||||
{hasAccount && (
|
||||
{hasAccount && isSharedVaultsEnabled && (
|
||||
<PreferencesGroup>
|
||||
<PreferencesSegment>
|
||||
<Title>Contacts</Title>
|
||||
@@ -172,7 +181,7 @@ const Vaults = observer(() => {
|
||||
</PreferencesSegment>
|
||||
</PreferencesGroup>
|
||||
)}
|
||||
{hasAccount && (
|
||||
{hasAccount && isSharedVaultsEnabled && (
|
||||
<PreferencesGroup>
|
||||
<PreferencesSegment>
|
||||
<Title>CollaborationID</Title>
|
||||
@@ -227,13 +236,15 @@ const Vaults = observer(() => {
|
||||
{canCreateMoreVaults ? (
|
||||
<div className="mt-2.5 flex gap-3">
|
||||
<Button label="Create Vault" onClick={createNewVault} />
|
||||
{hasAccount && <Button label="Create Shared Vault" onClick={createNewSharedVault} />}
|
||||
{hasAccount && isSharedVaultsEnabled && (
|
||||
<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>}
|
||||
text={<span>Please upgrade in order to increase your vault limit.</span>}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
+3
-1
@@ -17,6 +17,8 @@ type Props = {
|
||||
const VaultItem = ({ vault }: Props) => {
|
||||
const application = useApplication()
|
||||
|
||||
const canEnableCollaboration = application.hasAccount() && application.featuresController.isEntitledToSharedVaults()
|
||||
|
||||
const [isInviteModalOpen, setIsAddContactModalOpen] = useState(false)
|
||||
const closeInviteModal = () => setIsAddContactModalOpen(false)
|
||||
|
||||
@@ -128,7 +130,7 @@ const VaultItem = ({ vault }: Props) => {
|
||||
{isCurrentUserAdmin ? (
|
||||
vault.isSharedVaultListing() ? (
|
||||
<Button colorStyle="info" label="Invite Contacts" onClick={openInviteModal} />
|
||||
) : application.hasAccount() ? (
|
||||
) : canEnableCollaboration ? (
|
||||
<Button colorStyle="info" label="Enable Collaboration" onClick={convertToSharedVault} />
|
||||
) : null
|
||||
) : null}
|
||||
|
||||
+96
@@ -0,0 +1,96 @@
|
||||
import { useApplication } from '@/Components/ApplicationProvider'
|
||||
import Modal, { ModalAction } from '@/Components/Modal/Modal'
|
||||
import Spinner from '@/Components/Spinner/Spinner'
|
||||
import { SharedVaultUserServerHash, VaultListingInterface } from '@standardnotes/snjs'
|
||||
import { useCallback, useMemo, useState } from 'react'
|
||||
|
||||
const DesignateSurvivorModal = ({
|
||||
vault,
|
||||
members,
|
||||
closeModal,
|
||||
}: {
|
||||
vault: VaultListingInterface
|
||||
members: SharedVaultUserServerHash[]
|
||||
closeModal: () => void
|
||||
}) => {
|
||||
const application = useApplication()
|
||||
const [selectedSurvivor, setSelectedSurvivor] = useState<SharedVaultUserServerHash | null>(null)
|
||||
const [isDesignating, setIsDesignating] = useState(false)
|
||||
|
||||
const designateSelectedSurvivor = useCallback(async () => {
|
||||
if (!selectedSurvivor) {
|
||||
return
|
||||
}
|
||||
|
||||
if (!vault.isSharedVaultListing()) {
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
setIsDesignating(true)
|
||||
const result = await application.vaultUsers.designateSurvivor(vault, selectedSurvivor.user_uuid)
|
||||
if (result.isFailed()) {
|
||||
throw new Error(result.getError())
|
||||
}
|
||||
await application.sync.sync()
|
||||
closeModal()
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
} finally {
|
||||
setIsDesignating(false)
|
||||
}
|
||||
}, [application.sync, application.vaultUsers, closeModal, selectedSurvivor, vault])
|
||||
|
||||
const modalActions = useMemo(
|
||||
(): ModalAction[] => [
|
||||
{
|
||||
label: isDesignating ? <Spinner className="h-5 w-5 border-info-contrast" /> : 'Designate survivor',
|
||||
onClick: designateSelectedSurvivor,
|
||||
type: 'primary',
|
||||
mobileSlot: 'right',
|
||||
disabled: !selectedSurvivor || isDesignating,
|
||||
hidden: members.length === 0,
|
||||
},
|
||||
{
|
||||
label: 'Cancel',
|
||||
onClick: closeModal,
|
||||
type: 'cancel',
|
||||
mobileSlot: 'left',
|
||||
},
|
||||
],
|
||||
[closeModal, designateSelectedSurvivor, isDesignating, members.length, selectedSurvivor],
|
||||
)
|
||||
|
||||
return (
|
||||
<Modal title="Designate survivor" close={closeModal} actions={modalActions} className="px-4.5 py-4">
|
||||
<div className="flex flex-col gap-3">
|
||||
{members.map((member) => {
|
||||
const isSelected = selectedSurvivor?.uuid === member.uuid
|
||||
const contact = application.contacts.findContactForServerUser(member)
|
||||
if (!contact) {
|
||||
return null
|
||||
}
|
||||
const isOwner = application.vaultUsers.isVaultUserOwner(member)
|
||||
if (isOwner) {
|
||||
return null
|
||||
}
|
||||
return (
|
||||
<label className="grid grid-cols-[auto_1fr] gap-x-3 gap-y-0.5" key={member.uuid}>
|
||||
<input
|
||||
className="h-4 w-4 self-center accent-info"
|
||||
type="radio"
|
||||
name="survivor"
|
||||
checked={isSelected}
|
||||
onClick={() => setSelectedSurvivor(member)}
|
||||
/>
|
||||
<div className="col-start-2 text-sm font-semibold">{contact.name}</div>
|
||||
<div className="col-start-2 opacity-90">{contact.contactUuid}</div>
|
||||
</label>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</Modal>
|
||||
)
|
||||
}
|
||||
|
||||
export default DesignateSurvivorModal
|
||||
+30
-1
@@ -1,8 +1,10 @@
|
||||
import { useCallback } from 'react'
|
||||
import { useCallback, useState } from 'react'
|
||||
import { useApplication } from '@/Components/ApplicationProvider'
|
||||
import { SharedVaultUserServerHash, VaultListingInterface } from '@standardnotes/snjs'
|
||||
import Icon from '@/Components/Icon/Icon'
|
||||
import Button from '@/Components/Button/Button'
|
||||
import ModalOverlay from '@/Components/Modal/ModalOverlay'
|
||||
import DesignateSurvivorModal from './DesignateSurvivorModal'
|
||||
|
||||
export const VaultModalMembers = ({
|
||||
members,
|
||||
@@ -27,9 +29,30 @@ export const VaultModalMembers = ({
|
||||
[application.vaultUsers, vault, onChange],
|
||||
)
|
||||
|
||||
const vaultHasNoDesignatedSurvivor = vault.isSharedVaultListing() && !vault.sharing.designatedSurvivor
|
||||
const [isDesignateSurvivorModalOpen, setIsDesignateSurvivorModalOpen] = useState(false)
|
||||
const openDesignateSurvivorModal = () => setIsDesignateSurvivorModalOpen(true)
|
||||
const closeDesignateSurvivorModal = () => setIsDesignateSurvivorModalOpen(false)
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="mb-3 text-lg">Vault Members</div>
|
||||
{vaultHasNoDesignatedSurvivor && members.length > 1 && isCurrentUserAdmin && (
|
||||
<div className="bg-danger-faded mb-3 grid grid-cols-[auto,1fr] gap-x-[0.65rem] gap-y-0.5 overflow-hidden rounded p-2.5 text-danger">
|
||||
<Icon type="warning" className="place-self-center" />
|
||||
<div className="text-base font-semibold">No designated survivor</div>
|
||||
<div className="col-start-2">
|
||||
Vaults that have no designated survivor will be deleted when the owner account is deleted. In order to
|
||||
ensure that no data is lost, please designate a survivor who will be transferred ownership of the vault.
|
||||
</div>
|
||||
<Button small className="col-start-2 mt-1.5" onClick={openDesignateSurvivorModal}>
|
||||
Designate survivor
|
||||
</Button>
|
||||
<ModalOverlay isOpen={isDesignateSurvivorModalOpen} close={closeDesignateSurvivorModal}>
|
||||
<DesignateSurvivorModal vault={vault} members={members} closeModal={closeDesignateSurvivorModal} />
|
||||
</ModalOverlay>
|
||||
</div>
|
||||
)}
|
||||
<div className="space-y-3.5">
|
||||
{members.map((member) => {
|
||||
const isMemberVaultOwner = application.vaultUsers.isVaultUserOwner(member)
|
||||
@@ -55,6 +78,12 @@ export const VaultModalMembers = ({
|
||||
Untrusted
|
||||
</div>
|
||||
)}
|
||||
{member.is_designated_survivor && (
|
||||
<div className="flex items-center gap-1 rounded bg-info px-1 py-0.5 text-xs text-success-contrast">
|
||||
<Icon type="security" size="small" />
|
||||
Designated survivor
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="col-start-2 row-start-2">{permission}</div>
|
||||
{isCurrentUserAdmin && !isMemberVaultOwner && (
|
||||
|
||||
+9
-1
@@ -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}>
|
||||
|
||||
@@ -124,7 +124,7 @@ const Navigation = forwardRef<HTMLDivElement, Props>(({ application, className,
|
||||
)}
|
||||
<PreferencesButton openPreferences={() => application.preferencesController.openPreferences()} />
|
||||
<QuickSettingsButton application={application} isMobileNavigation />
|
||||
{application.featuresController.isEntitledToVaults() && <VaultSelectionButton isMobileNavigation />}
|
||||
{application.featuresController.isVaultsEnabled() && <VaultSelectionButton isMobileNavigation />}
|
||||
</div>
|
||||
{children}
|
||||
</div>
|
||||
|
||||
@@ -81,7 +81,7 @@ const TagContextMenu = ({ navigationController, isEntitledToFolders, selectedTag
|
||||
iconGridClassName="max-h-30"
|
||||
/>
|
||||
<HorizontalSeparator classes="my-2" />
|
||||
{application.featuresController.isEntitledToVaults() && (
|
||||
{application.featuresController.isVaultsEnabled() && (
|
||||
<AddToVaultMenuOption iconClassName="mr-2 text-neutral" items={[selectedTag]} />
|
||||
)}
|
||||
<MenuItem className={'justify-between py-1.5'} onClick={onClickStar}>
|
||||
|
||||
@@ -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],
|
||||
@@ -117,7 +120,7 @@ const AddToVaultMenuOption = ({
|
||||
setIsSubMenuOpen((isOpen) => !isOpen)
|
||||
}, [])
|
||||
|
||||
if (!application.featuresController.isEntitledToVaults()) {
|
||||
if (!application.featuresController.isVaultsEnabled()) {
|
||||
return null
|
||||
}
|
||||
|
||||
|
||||
@@ -130,7 +130,12 @@ export class FeaturesController extends AbstractViewController implements Intern
|
||||
return status === FeatureStatus.Entitled
|
||||
}
|
||||
|
||||
isEntitledToVaults(): boolean {
|
||||
isVaultsEnabled(): boolean {
|
||||
const enabled = this.features.isExperimentalFeatureEnabled(NativeFeatureIdentifier.TYPES.Vaults)
|
||||
return featureTrunkVaultsEnabled() || enabled
|
||||
}
|
||||
|
||||
isEntitledToSharedVaults(): boolean {
|
||||
const status = this.features.getFeatureStatus(
|
||||
NativeFeatureIdentifier.create(NativeFeatureIdentifier.TYPES.SharedVaults).getValue(),
|
||||
)
|
||||
|
||||
@@ -15,6 +15,7 @@ import {
|
||||
InternalFeatureService,
|
||||
InternalFeature,
|
||||
PreferenceServiceInterface,
|
||||
Result,
|
||||
} from '@standardnotes/snjs'
|
||||
import { FilesController } from './FilesController'
|
||||
import { ItemListController } from './ItemList/ItemListController'
|
||||
@@ -70,7 +71,8 @@ describe('LinkingController', () => {
|
||||
application.getPreference = jest.fn()
|
||||
application.addSingleEventObserver = jest.fn()
|
||||
application.sync.sync = jest.fn()
|
||||
application.featuresController.isEntitledToVaults = jest.fn().mockReturnValue(true)
|
||||
application.featuresController.isVaultsEnabled = jest.fn().mockReturnValue(true)
|
||||
application.featuresController.isEntitledToSharedVaults = jest.fn().mockReturnValue(true)
|
||||
|
||||
Object.defineProperty(application, 'items', { value: {} as jest.Mocked<ItemManagerInterface> })
|
||||
|
||||
@@ -246,7 +248,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',
|
||||
|
||||
@@ -208,12 +208,15 @@ export class LinkingController extends AbstractViewController implements Interna
|
||||
const linkNoteAndFile = async (note: SNNote, file: FileItem) => {
|
||||
const updatedFile = await this.mutator.associateFileWithNote(file, note)
|
||||
|
||||
if (this.featuresController.isEntitledToVaults()) {
|
||||
if (this.featuresController.isVaultsEnabled()) {
|
||||
if (updatedFile) {
|
||||
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())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -305,10 +308,12 @@ export class LinkingController extends AbstractViewController implements Interna
|
||||
const itemVault = this.vaults.getItemVault(activeItem)
|
||||
|
||||
if (itemVault) {
|
||||
const movedTag = await this.vaults.moveItemToVault(itemVault, newTag)
|
||||
if (!movedTag) {
|
||||
throw new Error('Failed to move tag to item vault')
|
||||
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)
|
||||
|
||||
@@ -9,7 +9,8 @@ export function featureTrunkEnabled(trunk: FeatureTrunkName): boolean {
|
||||
}
|
||||
|
||||
export function featureTrunkVaultsEnabled(): boolean {
|
||||
return InternalFeatureService.get().isFeatureEnabled(InternalFeature.Vaults)
|
||||
// return InternalFeatureService.get().isFeatureEnabled(InternalFeature.Vaults)
|
||||
return false
|
||||
}
|
||||
|
||||
export function featureTrunkHomeServerEnabled(): boolean {
|
||||
|
||||
@@ -23,7 +23,7 @@ export const useItemVaultInfo = (item: DecryptedItemInterface): ItemVaultInfo =>
|
||||
const [sharedByContact, setSharedByContact] = useState<TrustedContactInterface>()
|
||||
|
||||
const updateInfo = useCallback(() => {
|
||||
if (!application.featuresController.isEntitledToVaults()) {
|
||||
if (!application.featuresController.isVaultsEnabled()) {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user