Compare commits

...
Author SHA1 Message Date
StandardNotes CI f0566c7d07 chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].8
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
2023-09-13 13:21:53 +00:00
Mo bea266a69a chore(temp): disable history tests 2023-09-13 07:47:05 -05:00
Mo e079e0ec86 chore(ci): notify only if publish succeeds 2023-09-13 07:24:37 -05:00
Mo 5fc98b9de5 chore: bump version 2023-09-13 07:12:12 -05:00
Mo 94be549f30 chore: add e2e tests for case insensitive aad compare [no ci] 2023-09-13 07:10:58 -05:00
Aman Harwara 1275a2e148 chore: remember previous multiple vault selection when switching back from single to multiple 2023-09-13 16:50:35 +05:30
Aman Harwara 9ebb095aaa chore: fix typos 2023-09-13 16:37:45 +05:30
Aman Harwara 5517c96822 chore: fix vault selection menu not auto-updating on vault changes 2023-09-13 16:15:45 +05:30
Aman Harwara a07fa23c70 refactor: radio button group value generic 2023-09-13 15:59:57 +05:30
Aman Harwara 7c9cc821a2 chore: don't show listed menu option if note is in shared vault 2023-09-13 15:48:38 +05:30
StandardNotes CI 327abbf5ae chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].7
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].9
 - @standardnotes/[email protected]
2023-09-12 22:18:14 +00:00
Mo 09ba8fb99b chore: add mechanism for legacy uuid aad 2023-09-12 16:47:24 -05:00
Aman Harwara 6978b56960 chore: only allow vault admins to invite more contacts 2023-09-13 02:17:15 +05:30
Aman Harwara ab2707d122 chore: fix issue where vault admin wouldn't show up in members list and improve styles 2023-09-13 02:11:24 +05:30
Aman Harwara 2a0fdeedd6 chore: show spinner when loading vault collaboration info 2023-09-13 01:46:26 +05:30
Aman Harwara b17b25666b chore: move vault key options inside advanced options accordion 2023-09-13 01:35:21 +05:30
Aman Harwara 9157dc1364 chore: show key option descriptions & improve style 2023-09-13 00:10:32 +05:30
Aman Harwara 464af45b50 chore: fix icon picker state not changing in tag context menu 2023-09-12 23:43:31 +05:30
Aman Harwara 56642422e4 chore: correctly change exclusively selected vault in footer option 2023-09-12 23:41:01 +05:30
Karol Sójko 43fc95a7ac chore: e2e test for fetching revisions (#2480) 2023-09-12 15:56:48 +02:00
Aman Harwara 57265d1d4e chore: fix current tag selection on vault display option change 2023-09-12 16:42:07 +05:30
Aman Harwara 6f6600ebd6 chore: change vault name badge style 2023-09-12 16:42:07 +05:30
Aman Harwara 2ed68ada6b chore: stop "last edited by" badge flickering when editing a note 2023-09-12 16:42:07 +05:30
Aman Harwara 53785c7100 chore: correctly refresh vault info badge 2023-09-12 16:42:07 +05:30
StandardNotes CI ada155a8c9 chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].8
 - @standardnotes/[email protected]
2023-09-12 08:14:28 +00:00
Karol Sójko d6a3e69d8c chore: remove the option to trigger transition from the UI (#2489) 2023-09-12 09:25:05 +02:00
StandardNotes CI e50cc591c0 chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
2023-09-11 14:08:11 +00:00
Aman Harwara 5311f1e098 chore: reset values changed for testing 2023-09-11 18:36:51 +05:30
Aman Harwara 7ca2fa8eab chore: fix preferences count bubble size & position 2023-09-11 18:36:11 +05:30
Aman Harwara 51c095efa9 chore: update checklist editor 2023-09-11 15:11:11 +05:30
StandardNotes CI 0eb30598df chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
2023-09-11 09:35:07 +00:00
Aman Harwara 4907011ff2 chore: add labels to edit contact modal instead of placeholders for better ux 2023-09-11 14:08:49 +05:30
Aman Harwara 98dff43ab5 chore: improve contacts list ui 2023-09-11 14:02:32 +05:30
Aman Harwara 5fc8f6e6a0 chore: upgrade lexical 2023-09-11 13:22:05 +05:30
Aman Harwara 95c0352464 chore: fix error when multiple files are moved into another vault 2023-09-10 20:56:09 +05:30
StandardNotes CI 725a40b833 chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
2023-09-08 15:22:51 +00:00
Aman Harwara 0447b450d7 chore: improve vault ui element sizing 2023-09-08 20:00:32 +05:30
Aman Harwara dfa994ee8d chore: fix initial value for count bubble 2023-09-08 19:35:38 +05:30
Aman Harwara 126e9abd9c chore: hide collaboration options if no account 2023-09-08 19:14:41 +05:30
Karol Sójko 5faeaaf330 chore: fix input params for e2e test suites 2023-09-08 15:22:03 +02:00
Aman Harwara 22d6fcf973 chore: remove testing default 2023-09-08 18:42:53 +05:30
Aman Harwara 2e364dc016 chore: show preferences notification count bubble on mobile 2023-09-08 18:35:57 +05:30
Karol Sójko 986985b7de chore: fix e2e test to wait for transition to finish upon signout (#2487) 2023-09-08 14:49:42 +02:00
Aman Harwara 7c5f5c82a1 chore: improve note view vault info style (#2488) 2023-09-08 17:49:30 +05:30
StandardNotes CI 25c0d655ab chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
2023-09-08 10:40:54 +00:00
Aman Harwara bf3f6cec43 chore: catch error when fetching changelog 2023-09-08 13:49:32 +05:30
127 changed files with 1236 additions and 916 deletions
+11 -5
View File
@@ -199,6 +199,16 @@ jobs:
draft: false
files: packages/desktop/dist/*
- name: Publish Snap
continue-on-error: true
run: |
sudo snap install snapcraft --classic
snapcraft upload dist/standard-notes-${{ env.APP_VERSION }}-linux-amd64.snap --release stable,candidate,beta,edge
Notify:
runs-on: ubuntu-latest
needs: [Publish]
steps:
- name: Notify Release Update
uses: peter-evans/repository-dispatch@v2
with:
@@ -206,8 +216,4 @@ jobs:
repository: ${{ secrets.RELEASES_EVENT_RECEIVING_REPO }}
event-type: releases-updated-event
- name: Publish Snap
continue-on-error: true
run: |
sudo snap install snapcraft --classic
snapcraft upload dist/standard-notes-${{ env.APP_VERSION }}-linux-amd64.snap --release stable,candidate,beta,edge
+1 -1
View File
@@ -71,7 +71,7 @@ jobs:
github_token: ${{ secrets.CI_PAT_TOKEN }}
workflow_file_name: e2e-test-suite.yml
wait_interval: 30
client_payload: '{"snjs_image_tag": "${{ github.sha }}"}'
client_payload: '{"snjs_image_tag": "${{ github.sha }}", "author": "${{ github.actor }}", "ref_name": "app:${{ github.ref }}"}'
propagate_failure: true
trigger_workflow: true
wait_workflow: true
+1 -1
View File
@@ -48,7 +48,7 @@ jobs:
github_token: ${{ secrets.CI_PAT_TOKEN }}
workflow_file_name: e2e-test-suite.yml
wait_interval: 30
client_payload: '{"snjs_image_tag": "${{ github.sha }}"}'
client_payload: '{"snjs_image_tag": "${{ github.sha }}", "author": "${{ github.actor }}", "ref_name": "app:${{ github.ref }}"}'
propagate_failure: true
trigger_workflow: true
wait_workflow: true
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+4
View File
@@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.26.56](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-13)
**Note:** Version bump only for package @standardnotes/api
## [1.26.55](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-07)
**Note:** Version bump only for package @standardnotes/api
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/api",
"version": "1.26.55",
"version": "1.26.56",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
+28
View File
@@ -3,6 +3,34 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.1.215](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-13)
**Note:** Version bump only for package @standardnotes/clipper
## [1.1.214](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-12)
**Note:** Version bump only for package @standardnotes/clipper
## [1.1.213](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-12)
**Note:** Version bump only for package @standardnotes/clipper
## [1.1.212](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-11)
**Note:** Version bump only for package @standardnotes/clipper
## [1.1.211](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-11)
**Note:** Version bump only for package @standardnotes/clipper
## [1.1.210](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-08)
**Note:** Version bump only for package @standardnotes/clipper
## [1.1.209](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-08)
**Note:** Version bump only for package @standardnotes/clipper
## [1.1.208](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-07)
**Note:** Version bump only for package @standardnotes/clipper
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "@standardnotes/clipper",
"description": "Web clipper browser extension for Standard Notes",
"version": "1.1.208",
"version": "1.1.215",
"private": true,
"scripts": {
"build-mv2": "yarn clean && webpack --config ./webpack.config.prod.js",
+28
View File
@@ -3,6 +3,34 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [3.108.147](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-13)
**Note:** Version bump only for package @standardnotes/desktop
## [3.108.146](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-12)
**Note:** Version bump only for package @standardnotes/desktop
## [3.108.145](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-12)
**Note:** Version bump only for package @standardnotes/desktop
## [3.108.144](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-11)
**Note:** Version bump only for package @standardnotes/desktop
## [3.108.143](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-11)
**Note:** Version bump only for package @standardnotes/desktop
## [3.108.142](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-08)
**Note:** Version bump only for package @standardnotes/desktop
## [3.108.141](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-08)
**Note:** Version bump only for package @standardnotes/desktop
## [3.108.140](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-07)
**Note:** Version bump only for package @standardnotes/desktop
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "@standardnotes/desktop",
"main": "./app/dist/index.js",
"version": "3.108.140",
"version": "3.108.147",
"license": "CC BY-NC-SA 4.0",
"author": "Standard Notes.",
"private": true,
+8
View File
@@ -3,6 +3,14 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.21.78](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-13)
**Note:** Version bump only for package @standardnotes/encryption
## [1.21.77](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-12)
**Note:** Version bump only for package @standardnotes/encryption
## [1.21.76](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-07)
**Note:** Version bump only for package @standardnotes/encryption
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/encryption",
"version": "1.21.76",
"version": "1.21.78",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
@@ -17,6 +17,7 @@ import {
} from './../../../../Types/EncryptedParameters'
import { DecryptedParameters } from '../../../../Types/DecryptedParameters'
import { DeriveHashingKeyUseCase } from '../Hash/DeriveHashingKey'
import { V004Components } from '../../V004AlgorithmTypes'
export class GenerateDecryptedParametersUseCase {
private base64DataUsecase = new CreateConsistentBase64JsonPayloadUseCase(this.crypto)
@@ -39,7 +40,7 @@ export class GenerateDecryptedParametersUseCase {
}
}
const contentResult = this.decryptContent(encrypted, contentKeyResult.contentKey)
const contentResult = this.decryptContent(encrypted, contentKeyResult.decrypted)
if (!contentResult) {
return {
uuid: encrypted.uuid,
@@ -54,17 +55,17 @@ export class GenerateDecryptedParametersUseCase {
hashingKey,
{
additionalData: contentKeyResult.components.additionalData,
plaintext: contentKeyResult.contentKey,
plaintext: contentKeyResult.decrypted,
},
{
additionalData: contentResult.components.additionalData,
plaintext: contentResult.content,
plaintext: contentResult.decrypted,
},
)
return {
uuid: encrypted.uuid,
content: JSON.parse(contentResult.content),
content: JSON.parse(contentResult.decrypted),
signatureData: signatureVerificationResult,
}
}
@@ -72,34 +73,7 @@ export class GenerateDecryptedParametersUseCase {
private decryptContent(encrypted: EncryptedOutputParameters, contentKey: string) {
const contentComponents = deconstructEncryptedPayloadString(encrypted.content)
const contentAuthenticatedData = this.stringToAuthenticatedDataUseCase.execute(
contentComponents.authenticatedData,
{
u: encrypted.uuid,
v: encrypted.version,
ksi: encrypted.key_system_identifier,
svu: encrypted.shared_vault_uuid,
},
)
const authenticatedDataString = this.base64DataUsecase.execute(contentAuthenticatedData)
const content = this.crypto.xchacha20Decrypt(
contentComponents.ciphertext,
contentComponents.nonce,
contentKey,
authenticatedDataString,
)
if (!content) {
return null
}
return {
content,
components: contentComponents,
authenticatedDataString,
}
return this.decrypt(encrypted, contentComponents, contentKey)
}
private decryptContentKey(
@@ -108,32 +82,37 @@ export class GenerateDecryptedParametersUseCase {
) {
const contentKeyComponents = deconstructEncryptedPayloadString(encrypted.enc_item_key)
const contentKeyAuthenticatedData = this.stringToAuthenticatedDataUseCase.execute(
contentKeyComponents.authenticatedData,
{
u: encrypted.uuid,
v: encrypted.version,
ksi: encrypted.key_system_identifier,
svu: encrypted.shared_vault_uuid,
},
)
return this.decrypt(encrypted, contentKeyComponents, key.itemsKey)
}
const authenticatedDataString = this.base64DataUsecase.execute(contentKeyAuthenticatedData)
private decrypt(encrypted: EncryptedOutputParameters, components: V004Components, key: string) {
const rawAuthenticatedData = this.stringToAuthenticatedDataUseCase.executeRaw(components.authenticatedData)
const contentKey = this.crypto.xchacha20Decrypt(
contentKeyComponents.ciphertext,
contentKeyComponents.nonce,
key.itemsKey,
const doesRawContainLegacyUppercaseUuid = /[A-Z]/.test(rawAuthenticatedData.u)
const authenticatedData = this.stringToAuthenticatedDataUseCase.execute(components.authenticatedData, {
u: doesRawContainLegacyUppercaseUuid ? encrypted.uuid.toUpperCase() : encrypted.uuid,
v: encrypted.version,
ksi: encrypted.key_system_identifier,
svu: encrypted.shared_vault_uuid,
})
const authenticatedDataString = this.base64DataUsecase.execute(authenticatedData)
const decrypted = this.crypto.xchacha20Decrypt(
components.ciphertext,
components.nonce,
key,
authenticatedDataString,
)
if (!contentKey) {
if (!decrypted) {
return null
}
return {
contentKey,
components: contentKeyComponents,
decrypted,
components: components,
authenticatedDataString,
}
}
@@ -16,4 +16,9 @@ export class StringToAuthenticatedDataUseCase {
...override,
})
}
executeRaw(rawAuthenticatedData: string): RootKeyEncryptedAuthenticatedData | ItemAuthenticatedData {
const base = JSON.parse(this.crypto.base64Decode(rawAuthenticatedData))
return base
}
}
+8
View File
@@ -3,6 +3,14 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.28.89](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-13)
**Note:** Version bump only for package @standardnotes/filepicker
## [1.28.88](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-12)
**Note:** Version bump only for package @standardnotes/filepicker
## [1.28.87](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-07)
**Note:** Version bump only for package @standardnotes/filepicker
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/filepicker",
"version": "1.28.87",
"version": "1.28.89",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
+8
View File
@@ -3,6 +3,14 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.17.8](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-13)
**Note:** Version bump only for package @standardnotes/files
## [1.17.7](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-12)
**Note:** Version bump only for package @standardnotes/files
## [1.17.6](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-07)
**Note:** Version bump only for package @standardnotes/files
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/files",
"version": "1.17.6",
"version": "1.17.8",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
+28
View File
@@ -3,6 +3,34 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [3.56.126](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-13)
**Note:** Version bump only for package @standardnotes/mobile
## [3.56.125](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-12)
**Note:** Version bump only for package @standardnotes/mobile
## [3.56.124](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-12)
**Note:** Version bump only for package @standardnotes/mobile
## [3.56.123](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-11)
**Note:** Version bump only for package @standardnotes/mobile
## [3.56.122](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-11)
**Note:** Version bump only for package @standardnotes/mobile
## [3.56.121](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-08)
**Note:** Version bump only for package @standardnotes/mobile
## [3.56.120](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-08)
**Note:** Version bump only for package @standardnotes/mobile
## [3.56.119](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-07)
**Note:** Version bump only for package @standardnotes/mobile
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/mobile",
"version": "3.56.119",
"version": "3.56.126",
"author": "Standard Notes.",
"private": true,
"license": "CC BY-NC-SA 4.0",
+4
View File
@@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.47.14](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-13)
**Note:** Version bump only for package @standardnotes/models
## [1.47.13](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-07)
**Note:** Version bump only for package @standardnotes/models
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/models",
"version": "1.47.13",
"version": "1.47.14",
"publishConfig": {
"access": "public"
},
@@ -11,7 +11,7 @@ import { ExclusiveVaultCriteriaValidator } from '../../Display/Validator/Exclusi
import { HiddenContentCriteriaValidator } from '../../Display/Validator/HiddenContentCriteriaValidator'
import { CustomFilterCriteriaValidator } from '../../Display/Validator/CustomFilterCriteriaValidator'
import { AnyDisplayOptions, VaultDisplayOptions } from '../../Display'
import { isExclusioanaryOptionsValue } from '../../Display/VaultDisplayOptionsTypes'
import { isExclusionaryOptionsValue } from '../../Display/VaultDisplayOptionsTypes'
import { ContentType } from '@standardnotes/domain-core'
type AllNotesUuidSignifier = undefined
@@ -83,7 +83,7 @@ export class ItemCounter implements SNIndex {
if (this.vaultDisplayOptions) {
const options = this.vaultDisplayOptions.getOptions()
if (isExclusioanaryOptionsValue(options)) {
if (isExclusionaryOptionsValue(options)) {
filters.push(new ExcludeVaultsCriteriaValidator([...options.exclude, ...options.locked], element))
} else {
filters.push(new ExclusiveVaultCriteriaValidator(options.exclusive, element))
@@ -11,7 +11,7 @@ import { ExcludeVaultsCriteriaValidator } from './Validator/ExcludeVaultsCriteri
import { ExclusiveVaultCriteriaValidator } from './Validator/ExclusiveVaultCriteriaValidator'
import { HiddenContentCriteriaValidator } from './Validator/HiddenContentCriteriaValidator'
import { VaultDisplayOptions } from './VaultDisplayOptions'
import { isExclusioanaryOptionsValue } from './VaultDisplayOptionsTypes'
import { isExclusionaryOptionsValue } from './VaultDisplayOptionsTypes'
export class ItemDisplayController<I extends DisplayItem, O extends AnyDisplayOptions = GenericDisplayOptions> {
private sortMap: UuidToSortedPositionMap = {}
@@ -61,7 +61,7 @@ export class ItemDisplayController<I extends DisplayItem, O extends AnyDisplayOp
if (this.vaultOptions) {
const options = this.vaultOptions.getOptions()
if (isExclusioanaryOptionsValue(options)) {
if (isExclusionaryOptionsValue(options)) {
filters.push(new ExcludeVaultsCriteriaValidator([...options.exclude, ...options.locked], element))
} else {
filters.push(new ExclusiveVaultCriteriaValidator(options.exclusive, element))
@@ -1,10 +1,10 @@
import { VaultListingInterface } from '../../Syncable/VaultListing/VaultListingInterface'
import { uniqueArray } from '@standardnotes/utils'
import {
ExclusioanaryOptions,
ExclusionaryOptions,
ExclusiveOptions,
VaultDisplayOptionsPersistable,
isExclusioanaryOptionsValue,
isExclusionaryOptionsValue,
} from './VaultDisplayOptionsTypes'
import { KeySystemIdentifier } from '../../Syncable/KeySystemRootKey/KeySystemIdentifier'
@@ -13,14 +13,14 @@ function KeySystemIdentifiers(vaults: VaultListingInterface[]): KeySystemIdentif
}
export class VaultDisplayOptions {
constructor(private readonly options: ExclusioanaryOptions | ExclusiveOptions) {}
constructor(private readonly options: ExclusionaryOptions | ExclusiveOptions) {}
public getOptions(): ExclusioanaryOptions | ExclusiveOptions {
public getOptions(): ExclusionaryOptions | ExclusiveOptions {
return this.options
}
public getExclusivelyShownVault(): KeySystemIdentifier {
if (isExclusioanaryOptionsValue(this.options)) {
if (isExclusionaryOptionsValue(this.options)) {
throw new Error('Not in exclusive display mode')
}
@@ -28,11 +28,11 @@ export class VaultDisplayOptions {
}
public isInExclusiveDisplayMode(): boolean {
return !isExclusioanaryOptionsValue(this.options)
return !isExclusionaryOptionsValue(this.options)
}
public isVaultExplicitelyExcluded(vault: VaultListingInterface): boolean {
if (isExclusioanaryOptionsValue(this.options)) {
public isVaultExplicitlyExcluded(vault: VaultListingInterface): boolean {
if (isExclusionaryOptionsValue(this.options)) {
return this.options.exclude.some((excludedVault) => excludedVault === vault.systemIdentifier)
} else if (this.options.exclusive) {
return this.options.exclusive !== vault.systemIdentifier
@@ -42,18 +42,18 @@ export class VaultDisplayOptions {
}
isVaultExclusivelyShown(vault: VaultListingInterface): boolean {
return !isExclusioanaryOptionsValue(this.options) && this.options.exclusive === vault.systemIdentifier
return !isExclusionaryOptionsValue(this.options) && this.options.exclusive === vault.systemIdentifier
}
isVaultDisabledOrLocked(vault: VaultListingInterface): boolean {
if (isExclusioanaryOptionsValue(this.options)) {
if (isExclusionaryOptionsValue(this.options)) {
const matchingLocked = this.options.locked.find((lockedVault) => lockedVault === vault.systemIdentifier)
if (matchingLocked) {
return true
}
}
return this.isVaultExplicitelyExcluded(vault)
return this.isVaultExplicitlyExcluded(vault)
}
getPersistableValue(): VaultDisplayOptionsPersistable {
@@ -61,7 +61,7 @@ export class VaultDisplayOptions {
}
newOptionsByIntakingLockedVaults(lockedVaults: VaultListingInterface[]): VaultDisplayOptions {
if (isExclusioanaryOptionsValue(this.options)) {
if (isExclusionaryOptionsValue(this.options)) {
return new VaultDisplayOptions({ exclude: this.options.exclude, locked: KeySystemIdentifiers(lockedVaults) })
} else {
return new VaultDisplayOptions({ exclusive: this.options.exclusive })
@@ -76,7 +76,7 @@ export class VaultDisplayOptions {
vaults: VaultListingInterface[],
lockedVaults: VaultListingInterface[],
): VaultDisplayOptions {
if (isExclusioanaryOptionsValue(this.options)) {
if (isExclusionaryOptionsValue(this.options)) {
return new VaultDisplayOptions({
exclude: uniqueArray([...this.options.exclude, ...KeySystemIdentifiers(vaults)]),
locked: KeySystemIdentifiers(lockedVaults),
@@ -93,7 +93,7 @@ export class VaultDisplayOptions {
vault: VaultListingInterface,
lockedVaults: VaultListingInterface[],
): VaultDisplayOptions {
if (isExclusioanaryOptionsValue(this.options)) {
if (isExclusionaryOptionsValue(this.options)) {
return new VaultDisplayOptions({
exclude: this.options.exclude.filter((excludedVault) => excludedVault !== vault.systemIdentifier),
locked: KeySystemIdentifiers(lockedVaults),
@@ -1,12 +1,12 @@
import { KeySystemIdentifier } from '../../Syncable/KeySystemRootKey/KeySystemIdentifier'
export type ExclusioanaryOptions = { exclude: KeySystemIdentifier[]; locked: KeySystemIdentifier[] }
export type ExclusionaryOptions = { exclude: KeySystemIdentifier[]; locked: KeySystemIdentifier[] }
export type ExclusiveOptions = { exclusive: KeySystemIdentifier }
export function isExclusioanaryOptionsValue(
options: ExclusioanaryOptions | ExclusiveOptions,
): options is ExclusioanaryOptions {
export function isExclusionaryOptionsValue(
options: ExclusionaryOptions | ExclusiveOptions,
): options is ExclusionaryOptions {
return 'exclude' in options || 'locked' in options
}
export type VaultDisplayOptionsPersistable = ExclusioanaryOptions | ExclusiveOptions
export type VaultDisplayOptionsPersistable = ExclusionaryOptions | ExclusiveOptions
+28
View File
@@ -3,6 +3,34 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.4.499](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-13)
**Note:** Version bump only for package @standardnotes/releases
## [1.4.498](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-12)
**Note:** Version bump only for package @standardnotes/releases
## [1.4.497](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-12)
**Note:** Version bump only for package @standardnotes/releases
## [1.4.496](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-11)
**Note:** Version bump only for package @standardnotes/releases
## [1.4.495](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-11)
**Note:** Version bump only for package @standardnotes/releases
## [1.4.494](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-08)
**Note:** Version bump only for package @standardnotes/releases
## [1.4.493](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-08)
**Note:** Version bump only for package @standardnotes/releases
## [1.4.492](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-07)
**Note:** Version bump only for package @standardnotes/releases
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/releases",
"version": "1.4.492",
"version": "1.4.499",
"license": "CC BY-NC-SA 4.0",
"main": "dist/releases.json",
"types": "dist/index.d.ts",
+12
View File
@@ -3,6 +3,18 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.64.16](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-13)
**Note:** Version bump only for package @standardnotes/services
## [1.64.15](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-12)
**Note:** Version bump only for package @standardnotes/services
## [1.64.14](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-12)
**Note:** Version bump only for package @standardnotes/services
## [1.64.13](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-07)
**Note:** Version bump only for package @standardnotes/services
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/services",
"version": "1.64.13",
"version": "1.64.16",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
@@ -13,8 +13,6 @@ import {
MfaServiceInterface,
GenerateUuid,
CreateDecryptedBackupFile,
GetTransitionStatus,
StartTransition,
} from '@standardnotes/services'
import { VaultLockServiceInterface } from './../VaultLock/VaultLockServiceInterface'
import { HistoryServiceInterface } from './../History/HistoryServiceInterface'
@@ -78,8 +76,6 @@ export interface ApplicationInterface {
get generateUuid(): GenerateUuid
get getHost(): GetHost
get setHost(): SetHost
get getTransitionStatus(): GetTransitionStatus
get startTransition(): StartTransition
// Services
get alerts(): AlertService
@@ -1,5 +1,4 @@
export enum InternalFeature {
Vaults = 'vaults',
HomeServer = 'home-server',
Transition = 'transition',
}
@@ -3,7 +3,7 @@ import { Result, SyncUseCaseInterface } from '@standardnotes/domain-core'
import { GetHost } from './GetHost'
export class IsApplicationUsingThirdPartyHost implements SyncUseCaseInterface<boolean> {
private readonly APPLICATION_DEFAULT_HOSTS = ['api.standardnotes.com', 'sync.standardnotes.org']
private readonly APPLICATION_DEFAULT_HOSTS = ['api.standardnotes.com', 'sync.standardnotes.org', 'localhost:3123']
private readonly FILES_DEFAULT_HOSTS = ['files.standardnotes.com']
@@ -1,34 +0,0 @@
import { HttpServiceInterface } from '@standardnotes/api'
import { GetTransitionStatus } from './GetTransitionStatus'
describe('GetTransitionStatus', () => {
let httpService: HttpServiceInterface
const createUseCase = () => new GetTransitionStatus(httpService)
beforeEach(() => {
httpService = {
get: jest.fn(),
} as unknown as HttpServiceInterface
})
it('should get transition status', async () => {
const useCase = createUseCase()
;(httpService.get as jest.Mock).mockResolvedValueOnce({ status: 200, data: { status: 'TO-DO' } })
const result = await useCase.execute()
expect(result.isFailed()).toBe(false)
expect(result.getValue()).toBe('TO-DO')
})
it('should fail to get transition status', async () => {
const useCase = createUseCase()
;(httpService.get as jest.Mock).mockResolvedValueOnce({ status: 400 })
const result = await useCase.execute()
expect(result.isFailed()).toBe(true)
})
})
@@ -1,17 +0,0 @@
import { Result, UseCaseInterface } from '@standardnotes/domain-core'
import { HttpServiceInterface } from '@standardnotes/api'
import { HttpStatusCode } from '@standardnotes/responses'
export class GetTransitionStatus implements UseCaseInterface<'TO-DO' | 'STARTED' | 'FINISHED' | 'FAILED'> {
constructor(private httpService: HttpServiceInterface) {}
async execute(): Promise<Result<'TO-DO' | 'STARTED' | 'FINISHED' | 'FAILED'>> {
const response = await this.httpService.get('/v1/users/transition-status')
if (response.status !== HttpStatusCode.Success) {
return Result.fail('Failed to get transition status')
}
return Result.ok((response.data as { status: 'TO-DO' | 'STARTED' | 'FINISHED' | 'FAILED' }).status)
}
}
@@ -1,33 +0,0 @@
import { HttpServiceInterface } from '@standardnotes/api'
import { StartTransition } from './StartTransition'
describe('StartTransition', () => {
let httpService: HttpServiceInterface
const createUseCase = () => new StartTransition(httpService)
beforeEach(() => {
httpService = {
post: jest.fn(),
} as unknown as HttpServiceInterface
})
it('should start transition', async () => {
const useCase = createUseCase()
;(httpService.post as jest.Mock).mockResolvedValueOnce({ status: 200 })
const result = await useCase.execute()
expect(result.isFailed()).toBe(false)
})
it('should fail to start transition', async () => {
const useCase = createUseCase()
;(httpService.post as jest.Mock).mockResolvedValueOnce({ status: 400 })
const result = await useCase.execute()
expect(result.isFailed()).toBe(true)
})
})
@@ -1,17 +0,0 @@
import { HttpServiceInterface } from '@standardnotes/api'
import { Result, UseCaseInterface } from '@standardnotes/domain-core'
import { HttpStatusCode } from '@standardnotes/responses'
export class StartTransition implements UseCaseInterface<void> {
constructor(private httpService: HttpServiceInterface) {}
async execute(): Promise<Result<void>> {
const response = await this.httpService.post('/v1/items/transition')
if (response.status !== HttpStatusCode.Success) {
return Result.fail('Failed to start transition')
}
return Result.ok()
}
}
@@ -243,7 +243,12 @@ export class VaultService
}
getItemVault(item: DecryptedItemInterface): VaultListingInterface | undefined {
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')
}
-2
View File
@@ -187,8 +187,6 @@ export * from './Sync/SyncOptions'
export * from './Sync/SyncQueueStrategy'
export * from './Sync/SyncServiceInterface'
export * from './Sync/SyncSource'
export * from './UseCase/Transition/GetTransitionStatus/GetTransitionStatus'
export * from './UseCase/Transition/StartTransition/StartTransition'
export * from './UseCase/ChangeAndSaveItem'
export * from './UseCase/DiscardItemsLocally'
export * from './UseCase/GenerateUuid'
+16
View File
@@ -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.
## [2.202.72](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-13)
**Note:** Version bump only for package @standardnotes/snjs
## [2.202.71](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-12)
**Note:** Version bump only for package @standardnotes/snjs
## [2.202.70](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-12)
**Note:** Version bump only for package @standardnotes/snjs
## [2.202.69](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-08)
**Note:** Version bump only for package @standardnotes/snjs
## [2.202.68](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-07)
**Note:** Version bump only for package @standardnotes/snjs
@@ -80,8 +80,6 @@ import {
GenerateUuid,
CreateDecryptedBackupFile,
CreateEncryptedBackupFile,
GetTransitionStatus,
StartTransition,
WebSocketsService,
} from '@standardnotes/services'
import {
@@ -1140,14 +1138,6 @@ export class SNApplication implements ApplicationInterface, AppGroupManagedAppli
return this.dependencies.get<SetHost>(TYPES.SetHost)
}
get getTransitionStatus(): GetTransitionStatus {
return this.dependencies.get<GetTransitionStatus>(TYPES.GetTransitionStatus)
}
get startTransition(): StartTransition {
return this.dependencies.get<StartTransition>(TYPES.StartTransition)
}
public get legacyApi(): LegacyApiService {
return this.dependencies.get<LegacyApiService>(TYPES.LegacyApiService)
}
@@ -140,8 +140,6 @@ import {
CreateDecryptedBackupFile,
CreateEncryptedBackupFile,
SyncLocalVaultsWithRemoteSharedVaults,
GetTransitionStatus,
StartTransition,
WebSocketsService,
} from '@standardnotes/services'
import { ItemManager } from '../../Services/Items/ItemManager'
@@ -155,7 +153,6 @@ import {
AuthenticatorApiService,
AuthenticatorServer,
HttpService,
HttpServiceInterface,
RevisionApiService,
RevisionServer,
SharedVaultInvitesServer,
@@ -1027,14 +1024,6 @@ export class Dependencies {
)
})
this.factory.set(TYPES.GetTransitionStatus, () => {
return new GetTransitionStatus(this.get<HttpServiceInterface>(TYPES.HttpService))
})
this.factory.set(TYPES.StartTransition, () => {
return new StartTransition(this.get<HttpServiceInterface>(TYPES.HttpService))
})
this.factory.set(TYPES.ListRevisions, () => {
return new ListRevisions(this.get<RevisionManager>(TYPES.RevisionManager))
})
@@ -171,8 +171,6 @@ export const TYPES = {
AuthorizeVaultDeletion: Symbol.for('AuthorizeVaultDeletion'),
CreateDecryptedBackupFile: Symbol.for('CreateDecryptedBackupFile'),
CreateEncryptedBackupFile: Symbol.for('CreateEncryptedBackupFile'),
GetTransitionStatus: Symbol.for('GetTransitionStatus'),
StartTransition: Symbol.for('StartTransition'),
// Mappers
SessionStorageMapper: Symbol.for('SessionStorageMapper'),
+23
View File
@@ -82,6 +82,29 @@ describe('001 protocol operations', () => {
expect(decrypted.content.text).to.equal('Decryptable Sentence')
})
it('should decrypt string with case-insensitive uuid check for aad', async () => {
/** If the server returns a lowercase uuid for the item, but the encrypted payload uses uppercase uuids, should still decrypt */
const uppercaseUuid = '959B042A-3892-461E-8C50-477C10C7C40A'
const lowercaseUuid = '959b042a-3892-461e-8c50-477c10c7c40a'
const payload = new DecryptedPayload({
uuid: uppercaseUuid,
content_type: ContentType.TYPES.Note,
content: FillItemContent({
title: 'hello',
text: 'world',
}),
})
const key = await protocol001.createItemsKey()
const params = await protocol001.generateEncryptedParametersAsync(payload, key)
params.uuid = lowercaseUuid
const decrypted = await protocol001.generateDecryptedParametersAsync(params, key)
expect(decrypted.content).to.eql(payload.content)
})
it('properly encrypts and decrypts', async () => {
const text = 'hello world'
const key = _key.masterKey
+23
View File
@@ -78,6 +78,29 @@ describe('002 protocol operations', () => {
expect(decrypted.content.text).to.equal('Decryptable Sentence')
})
it('should decrypt string with case-insensitive uuid check for aad', async () => {
/** If the server returns a lowercase uuid for the item, but the encrypted payload uses uppercase uuids, should still decrypt */
const uppercaseUuid = '959B042A-3892-461E-8C50-477C10C7C40A'
const lowercaseUuid = '959b042a-3892-461e-8c50-477c10c7c40a'
const payload = new DecryptedPayload({
uuid: uppercaseUuid,
content_type: ContentType.TYPES.Note,
content: FillItemContent({
title: 'hello',
text: 'world',
}),
})
const key = await protocol002.createItemsKey()
const params = await protocol002.generateEncryptedParametersAsync(payload, key)
params.uuid = lowercaseUuid
const decrypted = await protocol002.generateDecryptedParametersAsync(params, key)
expect(decrypted.content).to.eql(payload.content)
})
it('properly encrypts and decrypts strings', async () => {
const text = 'hello world'
const key = _key.masterKey
+23
View File
@@ -128,6 +128,29 @@ describe('004 protocol operations', function () {
expect(decrypted.content).to.eql(payload.content)
})
it('should decrypt string with case-insensitive uuid check for aad', async () => {
/** If the server returns a lowercase uuid for the item, but the encrypted payload uses uppercase uuids, should still decrypt */
const uppercaseUuid = '959B042A-3892-461E-8C50-477C10C7C40A'
const lowercaseUuid = '959b042a-3892-461e-8c50-477c10c7c40a'
const payload = new DecryptedPayload({
uuid: uppercaseUuid,
content_type: ContentType.TYPES.Note,
content: FillItemContent({
title: 'hello',
text: 'world',
}),
})
const key = await protocol004.createItemsKey()
const params = await protocol004.generateEncryptedParameters(payload, key)
params.uuid = lowercaseUuid
const decrypted = await protocol004.generateDecryptedParameters(params, key)
expect(decrypted.content).to.eql(payload.content)
})
it('modifying the uuid of the payload should fail to decrypt', async function () {
const payload = Factory.createNotePayload()
const key = await protocol004.createItemsKey()
@@ -1,5 +1,5 @@
export const VaultTests = {
enabled: true,
enabled: false,
exclusive: false,
files: [
'vaults/vaults.test.js',
+6 -6
View File
@@ -4,7 +4,7 @@ import { createNoteParams } from './lib/Items.js'
chai.use(chaiAsPromised)
const expect = chai.expect
describe('history manager', () => {
describe.skip('history manager', () => {
const largeCharacterChange = 25
let application, history, email, password
@@ -269,13 +269,13 @@ describe('history manager', () => {
const item = await Factory.createSyncedNote(application)
expect(item).to.be.ok
await Factory.sleep(2 * Factory.ServerRevisionCreationDelay)
await Factory.sleep(Factory.ServerRevisionCreationDelay)
const itemHistoryOrError = await application.listRevisions.execute({ itemUuid: item.uuid })
expect(itemHistoryOrError.isFailed()).to.equal(false)
const itemHistory = itemHistoryOrError.getValue()
expect(itemHistory.length).to.equal(1)
expect(itemHistory.length >= 1).to.be.true
})
it('should not create new revisions within the revision frequency window', async () => {
@@ -291,13 +291,13 @@ describe('history manager', () => {
syncOptions,
)
await Factory.sleep(2 * Factory.ServerRevisionCreationDelay)
await Factory.sleep(Factory.ServerRevisionCreationDelay)
const itemHistoryOrError = await application.listRevisions.execute({ itemUuid: item.uuid })
expect(itemHistoryOrError.isFailed()).to.equal(false)
const itemHistory = itemHistoryOrError.getValue()
expect(itemHistory.length).to.equal(1)
expect(itemHistory.length >= 1).to.be.true
})
it('should create new revisions outside the revision frequency window', async function () {
+39 -17
View File
@@ -4,10 +4,11 @@ import * as Collaboration from '../lib/Collaboration.js'
chai.use(chaiAsPromised)
const expect = chai.expect
describe.skip('shared vault revisions', function () {
describe('shared vault revisions', function () {
this.timeout(Factory.TwentySecondTimeout)
let context
let deinitContactContextFunction
beforeEach(async function () {
localStorage.clear()
@@ -20,34 +21,43 @@ describe.skip('shared vault revisions', function () {
afterEach(async function () {
await context.deinit()
if (deinitContactContextFunction) {
await deinitContactContextFunction()
}
localStorage.clear()
sinon.restore()
context = undefined
deinitContactContextFunction = undefined
})
it('should be able to access shared item revisions as third party user', async () => {
const { note, contactContext, deinitContactContext } =
await Collaboration.createSharedVaultWithAcceptedInviteAndNote(context)
deinitContactContextFunction = deinitContactContext
await Factory.sleep(Factory.ServerRevisionFrequency)
await context.changeNoteTitleAndSync(note, 'new title 1')
await Factory.sleep(Factory.ServerRevisionFrequency)
await context.changeNoteTitleAndSync(note, 'new title 2')
await Factory.sleep(Factory.ServerRevisionCreationDelay)
const itemHistoryOrError = await contactContext.application.listRevisions.execute({ itemUuid: note.uuid })
const itemHistory = itemHistoryOrError.getValue()
expect(itemHistory.length).to.equal(3)
const contactItemHistoryOrError = await contactContext.application.listRevisions.execute({ itemUuid: note.uuid })
const contactItemHistory = contactItemHistoryOrError.getValue()
expect(contactItemHistory.length >= 2).to.be.true
await deinitContactContext()
const itemHistoryOrError = await context.application.listRevisions.execute({ itemUuid: note.uuid })
const itemHistory = itemHistoryOrError.getValue()
expect(itemHistory.length >= 2).to.be.true
})
it('should create revision if other vault member edits node', async () => {
const { note, contactContext, deinitContactContext } =
await Collaboration.createSharedVaultWithAcceptedInviteAndNote(context)
deinitContactContextFunction = deinitContactContext
await Factory.sleep(Factory.ServerRevisionFrequency)
@@ -58,14 +68,13 @@ describe.skip('shared vault revisions', function () {
const itemHistoryOrError = await context.application.listRevisions.execute({ itemUuid: note.uuid })
const itemHistory = itemHistoryOrError.getValue()
expect(itemHistory.length).to.equal(2)
await deinitContactContext()
expect(itemHistory.length >= 2).to.be.true
})
it('should be able to decrypt revisions as third party user', async () => {
const { note, contactContext, deinitContactContext } =
await Collaboration.createSharedVaultWithAcceptedInviteAndNote(context)
deinitContactContextFunction = deinitContactContext
await Factory.sleep(Factory.ServerRevisionFrequency)
await context.changeNoteTitleAndSync(note, 'new title 1')
@@ -83,41 +92,54 @@ describe.skip('shared vault revisions', function () {
const fetched = fetchedOrError.getValue()
expect(fetched.payload.errorDecrypting).to.not.be.ok
expect(fetched.payload.content.title).to.equal('new title 1')
await deinitContactContext()
})
it('should not be able to access history of item removed from vault', async () => {
const { note, contactContext, deinitContactContext } =
await Collaboration.createSharedVaultWithAcceptedInviteAndNote(context)
deinitContactContextFunction = deinitContactContext
await Factory.sleep(Factory.ServerRevisionFrequency)
await context.changeNoteTitleAndSync(note, 'new title 1')
await Factory.sleep(Factory.ServerRevisionCreationDelay)
await Factory.sleep(Factory.ServerRevisionFrequency)
await context.vaults.removeItemFromVault(note)
const itemHistoryOrError = await contactContext.application.listRevisions.execute({ itemUuid: note.uuid })
expect(itemHistoryOrError.isFailed()).to.be.true
await Factory.sleep(Factory.ServerRevisionCreationDelay)
await deinitContactContext()
const itemHistoryOrError = await contactContext.application.listRevisions.execute({ itemUuid: note.uuid })
expect(itemHistoryOrError.isFailed()).to.be.false
const itemHistory = itemHistoryOrError.getValue()
expect(itemHistory.length).to.equal(0)
})
it('should not be able to access history of item after user is removed from vault', async () => {
const { note, sharedVault, contactContext, deinitContactContext } =
await Collaboration.createSharedVaultWithAcceptedInviteAndNote(context)
deinitContactContextFunction = deinitContactContext
await Factory.sleep(Factory.ServerRevisionFrequency)
await context.changeNoteTitleAndSync(note, 'new title 1')
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()
expect(itemHistory.length >= 1).to.be.true
await context.vaultUsers.removeUserFromSharedVault(sharedVault, contactContext.userUuid)
await Factory.sleep(Factory.ServerRevisionCreationDelay)
const itemHistoryOrError = await contactContext.application.listRevisions.execute({ itemUuid: note.uuid })
expect(itemHistoryOrError.isFailed()).to.be.true
itemHistoryOrError = await contactContext.application.listRevisions.execute({ itemUuid: note.uuid })
expect(itemHistoryOrError.isFailed()).to.be.false
itemHistory = itemHistoryOrError.getValue()
await deinitContactContext()
expect(itemHistory.length).to.equal(0)
})
})
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/snjs",
"version": "2.202.68",
"version": "2.202.72",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
+12
View File
@@ -3,6 +3,18 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.31.10](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-13)
**Note:** Version bump only for package @standardnotes/ui-services
## [1.31.9](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-12)
**Note:** Version bump only for package @standardnotes/ui-services
## [1.31.8](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-12)
**Note:** Version bump only for package @standardnotes/ui-services
## [1.31.7](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-07)
**Note:** Version bump only for package @standardnotes/ui-services
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/ui-services",
"version": "1.31.7",
"version": "1.31.10",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
@@ -13,7 +13,12 @@ import {
StorageKey,
VaultLockServiceEvent,
} from '@standardnotes/services'
import { VaultDisplayOptions, VaultDisplayOptionsPersistable, VaultListingInterface } from '@standardnotes/models'
import {
ExclusionaryOptions,
VaultDisplayOptions,
VaultDisplayOptionsPersistable,
VaultListingInterface,
} from '@standardnotes/models'
import { VaultDisplayServiceEvent } from './VaultDisplayServiceEvent'
import { AbstractUIService } from '../Abstract/AbstractUIService'
import { WebApplicationInterface } from '../WebApplication/WebApplicationInterface'
@@ -25,6 +30,7 @@ export class VaultDisplayService
extends AbstractUIService<VaultDisplayServiceEvent>
implements VaultDisplayServiceInterface, InternalEventHandlerInterface
{
previousMultipleSelectionOptions?: VaultDisplayOptions
options: VaultDisplayOptions
public exclusivelyShownVault: VaultListingInterface | undefined = undefined
@@ -37,7 +43,7 @@ export class VaultDisplayService
makeObservable(this, {
options: observable,
isVaultExplicitelyExcluded: observable,
isVaultExplicitlyExcluded: observable,
isVaultExclusivelyShown: observable,
exclusivelyShownVault: observable,
@@ -81,8 +87,8 @@ export class VaultDisplayService
return this.options
}
isVaultExplicitelyExcluded = (vault: VaultListingInterface): boolean => {
return this.options.isVaultExplicitelyExcluded(vault) ?? false
isVaultExplicitlyExcluded = (vault: VaultListingInterface): boolean => {
return this.options.isVaultExplicitlyExcluded(vault) ?? false
}
isVaultDisabledOrLocked(vault: VaultListingInterface): boolean {
@@ -101,10 +107,14 @@ export class VaultDisplayService
const vaults = this.application.vaults.getVaults()
const lockedVaults = this.application.vaultLocks.getLockedvaults()
const exclude = this.previousMultipleSelectionOptions
? (this.previousMultipleSelectionOptions.getOptions() as ExclusionaryOptions).exclude
: vaults
.map((vault) => vault.systemIdentifier)
.filter((identifier) => identifier !== this.exclusivelyShownVault?.systemIdentifier)
const newOptions = new VaultDisplayOptions({
exclude: vaults
.map((vault) => vault.systemIdentifier)
.filter((identifier) => identifier !== this.exclusivelyShownVault?.systemIdentifier),
exclude,
locked: lockedVaults.map((vault) => vault.systemIdentifier),
})
@@ -187,8 +197,16 @@ export class VaultDisplayService
}
private setVaultSelectionOptions = (options: VaultDisplayOptions) => {
const previousOptions = this.options
this.options = options
const changingFromMultipleToExclusive =
!previousOptions.isInExclusiveDisplayMode() && options.isInExclusiveDisplayMode()
if (changingFromMultipleToExclusive) {
this.previousMultipleSelectionOptions = previousOptions
}
if (this.isInExclusiveDisplayMode()) {
this.exclusivelyShownVault = this.application.vaults.getVault({
keySystemIdentifier: this.options.getExclusivelyShownVault(),
@@ -7,7 +7,7 @@ export interface VaultDisplayServiceInterface extends AbstractUIServiceInterface
getOptions(): VaultDisplayOptions
isVaultDisabledOrLocked(vault: VaultListingInterface): boolean
isVaultExplicitelyExcluded: (vault: VaultListingInterface) => boolean
isVaultExplicitlyExcluded: (vault: VaultListingInterface) => boolean
isVaultExclusivelyShown: (vault: VaultListingInterface) => boolean
isInExclusiveDisplayMode(): boolean
+28
View File
@@ -3,6 +3,34 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [3.172.18](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-13)
**Note:** Version bump only for package @standardnotes/web
## [3.172.17](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-12)
**Note:** Version bump only for package @standardnotes/web
## [3.172.16](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-12)
**Note:** Version bump only for package @standardnotes/web
## [3.172.15](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-11)
**Note:** Version bump only for package @standardnotes/web
## [3.172.14](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-11)
**Note:** Version bump only for package @standardnotes/web
## [3.172.13](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-08)
**Note:** Version bump only for package @standardnotes/web
## [3.172.12](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-08)
**Note:** Version bump only for package @standardnotes/web
## [3.172.11](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-07)
**Note:** Version bump only for package @standardnotes/web
+77
View File
@@ -1,5 +1,82 @@
{
"versions": [
{
"version": "3.172.18",
"title": "[3.172.18](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-13)",
"date": null,
"body": "**Note:** Version bump only for package @standardnotes/web",
"parsed": {
"_": [
"Note: Version bump only for package @standardnotes/web"
]
}
},
{
"version": "3.172.17",
"title": "[3.172.17](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-12)",
"date": null,
"body": "**Note:** Version bump only for package @standardnotes/web",
"parsed": {
"_": [
"Note: Version bump only for package @standardnotes/web"
]
}
},
{
"version": "3.172.16",
"title": "[3.172.16](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-12)",
"date": null,
"body": "**Note:** Version bump only for package @standardnotes/web",
"parsed": {
"_": [
"Note: Version bump only for package @standardnotes/web"
]
}
},
{
"version": "3.172.15",
"title": "[3.172.15](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-11)",
"date": null,
"body": "**Note:** Version bump only for package @standardnotes/web",
"parsed": {
"_": [
"Note: Version bump only for package @standardnotes/web"
]
}
},
{
"version": "3.172.14",
"title": "[3.172.14](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-11)",
"date": null,
"body": "**Note:** Version bump only for package @standardnotes/web",
"parsed": {
"_": [
"Note: Version bump only for package @standardnotes/web"
]
}
},
{
"version": "3.172.13",
"title": "[3.172.13](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-08)",
"date": null,
"body": "**Note:** Version bump only for package @standardnotes/web",
"parsed": {
"_": [
"Note: Version bump only for package @standardnotes/web"
]
}
},
{
"version": "3.172.12",
"title": "[3.172.12](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-08)",
"date": null,
"body": "**Note:** Version bump only for package @standardnotes/web",
"parsed": {
"_": [
"Note: Version bump only for package @standardnotes/web"
]
}
},
{
"version": "3.172.11",
"title": "[3.172.11](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-09-07)",
+6 -6
View File
@@ -1,9 +1,9 @@
{
"name": "@standardnotes/web",
"version": "3.172.11",
"version": "3.172.18",
"license": "CC BY-NC-SA 4.0",
"main": "dist/app.js",
"author": "Standard Notes",
"author": "Standard Notes.",
"private": true,
"files": [
"dist"
@@ -28,7 +28,7 @@
"@babel/plugin-transform-react-jsx": "^7.19.0",
"@babel/preset-env": "*",
"@babel/preset-typescript": "^7.21.5",
"@lexical/react": "0.12.0",
"@lexical/react": "0.12.2",
"@pmmmwh/react-refresh-webpack-plugin": "^0.5.10",
"@simplewebauthn/browser": "^7.1.0",
"@standardnotes/authenticator": "^2.4.0",
@@ -47,7 +47,7 @@
"@standardnotes/markdown-visual": "^1.3.2",
"@standardnotes/midnight-theme": "^1.4.6",
"@standardnotes/rich-text": "^1.8.7",
"@standardnotes/simple-task-editor": "^1.3.10",
"@standardnotes/simple-task-editor": "^1.5.8",
"@standardnotes/sncrypto-web": "workspace:*",
"@standardnotes/snjs": "workspace:*",
"@standardnotes/solarized-dark-theme": "^1.4.6",
@@ -76,7 +76,7 @@
"identity-obj-proxy": "^3.0.0",
"jest": "^29.3.1",
"jest-environment-jsdom": "^29.3.1",
"lexical": "0.12.0",
"lexical": "0.12.2",
"lint-staged": ">=13",
"mini-css-extract-plugin": "^2.7.2",
"minimatch": "^5.1.1",
@@ -109,7 +109,7 @@
},
"dependencies": {
"@ariakit/react": "^0.2.15",
"@lexical/headless": "0.12.0",
"@lexical/headless": "0.12.2",
"@radix-ui/react-slot": "^1.0.1",
"fast-diff": "^1.3.0"
}
@@ -5,7 +5,6 @@ export class DevMode {
constructor(private application: WebApplicationInterface) {
InternalFeatureService.get().enableFeature(InternalFeature.Vaults)
InternalFeatureService.get().enableFeature(InternalFeature.HomeServer)
InternalFeatureService.get().enableFeature(InternalFeature.Transition)
}
/** Valid only when running a mock event publisher on port 3124 */
@@ -17,7 +17,7 @@ const ListItemTags: FunctionComponent<Props> = ({ hideTags, tags }) => {
<div className="mt-1.5 flex flex-wrap gap-2 overflow-hidden text-sm lg:text-xs">
{tags.map((tag) => (
<span
className="inline-flex items-center rounded-sm bg-passive-4-opacity-variant px-1.5 py-1 text-foreground"
className="inline-flex items-center rounded bg-passive-4-opacity-variant px-1.5 py-1 text-foreground"
key={tag.uuid}
>
<Icon type={tag.iconString} className="mr-1 text-passive-1" size="small" />
@@ -1,8 +1,8 @@
import { FunctionComponent } from 'react'
import { useApplication } from '../ApplicationProvider'
import Icon from '../Icon/Icon'
import { DecryptedItemInterface, classNames } from '@standardnotes/snjs'
import VaultNameBadge from '../Vaults/VaultNameBadge'
import SharedByContactBadge from '../Vaults/SharedByContactBadge'
import { useItemVaultInfo } from '@/Hooks/useItemVaultInfo'
type Props = {
item: DecryptedItemInterface
@@ -10,35 +10,16 @@ type Props = {
}
const ListItemVaultInfo: FunctionComponent<Props> = ({ item, className }) => {
const application = useApplication()
const { vault, sharedByContact } = useItemVaultInfo(item)
if (!application.featuresController.isEntitledToVaults()) {
return null
}
if (application.items.isTemplateItem(item)) {
return null
}
const vault = application.vaults.getItemVault(item)
if (!vault) {
return null
}
const sharedByContact = application.sharedVaults.getItemSharedBy(item)
return (
<div className={classNames('flex flex-wrap items-center gap-2', className)}>
<VaultNameBadge vault={vault} />
{sharedByContact && (
<div title="Shared by contact" className="rounded bg-info px-1.5 py-1 text-neutral-contrast">
<span className="flex items-center" title="Shared by contact">
<Icon ariaLabel="Shared by contact" type="archive" className="mr-1 text-info-contrast" size="medium" />
<div className="text-center text-xs font-bold">{sharedByContact?.name}</div>
</span>
</div>
)}
{sharedByContact && <SharedByContactBadge contact={sharedByContact} />}
</div>
)
}
@@ -69,6 +69,10 @@ const FileMenuOptions: FunctionComponent<Props> = ({
closeMenu()
}, [closeMenu, toggleAppPane])
if (selectedFiles.length === 0) {
return <div className="text-center">No files selected</div>
}
return (
<>
{selectedFiles.length === 1 && (
@@ -5,6 +5,9 @@ import { useApplication } from '../ApplicationProvider'
import { useCommandService } from '../CommandProvider'
import Icon from '../Icon/Icon'
import StyledTooltip from '../StyledTooltip/StyledTooltip'
import { MutuallyExclusiveMediaQueryBreakpoints, useMediaQuery } from '@/Hooks/useMediaQuery'
import RoundIconButton from '../Button/RoundIconButton'
import CountBubble from '../Preferences/PreferencesComponents/CountBubble'
type Props = {
openPreferences: (openWhatsNew: boolean) => void
@@ -46,16 +49,23 @@ const PreferencesButton = ({ openPreferences }: Props) => {
})
}, [application.status])
const isMobileScreen = useMediaQuery(MutuallyExclusiveMediaQueryBreakpoints.sm)
if (isMobileScreen) {
return (
<div className="relative">
<RoundIconButton className="ml-2.5 bg-default" onClick={onClick} label="Go to preferences" icon="tune" />
<CountBubble position="right" count={bubbleCount} />
</div>
)
}
return (
<StyledTooltip label={`Open preferences (${shortcut})`}>
<button onClick={onClick} className="group relative flex h-full w-8 cursor-pointer items-center justify-center">
<div className="relative h-5">
<Icon type="tune" className="rounded group-hover:text-info" />
{bubbleCount && (
<div className="absolute bottom-full left-full aspect-square -translate-x-1/2 translate-y-1/2 rounded-full border border-info-contrast bg-info px-1.5 py-px text-[0.575rem] font-bold text-info-contrast">
{bubbleCount}
</div>
)}
<CountBubble position="right" count={bubbleCount} />
</div>
{isChangelogUnread && <div className="absolute right-0.5 top-0.5 h-2 w-2 rounded-full bg-info" />}
</button>
@@ -6,6 +6,7 @@ import StyledTooltip from '../StyledTooltip/StyledTooltip'
import VaultSelectionMenu from '../VaultSelectionMenu/VaultSelectionMenu'
import { useApplication } from '../ApplicationProvider'
import RoundIconButton from '../Button/RoundIconButton'
import { observer } from 'mobx-react-lite'
const VaultSelectionButton = ({ isMobileNavigation = false }: { isMobileNavigation?: boolean }) => {
const application = useApplication()
@@ -67,4 +68,4 @@ const VaultSelectionButton = ({ isMobileNavigation = false }: { isMobileNavigati
)
}
export default VaultSelectionButton
export default observer(VaultSelectionButton)
@@ -1,6 +1,6 @@
import { classNames } from '@standardnotes/utils'
import { EmojiString, Platform, VectorIconNameOrEmoji } from '@standardnotes/snjs'
import { ForwardedRef, forwardRef, useCallback, useMemo, useRef, useState } from 'react'
import { ForwardedRef, forwardRef, useCallback, useEffect, useMemo, useRef, useState } from 'react'
import Dropdown from '../Dropdown/Dropdown'
import { DropdownItem } from '../Dropdown/DropdownItem'
import { getEmojiLength } from './EmojiLength'
@@ -75,6 +75,10 @@ const IconPicker = ({ selectedValue, onIconChange, platform, className, useIconG
const [currentType, setCurrentType] = useState<IconPickerType>(isSelectedEmoji ? 'emoji' : 'icon')
const [emojiInputValue, setEmojiInputValue] = useState(isSelectedEmoji ? selectedValue : '')
useEffect(() => {
setEmojiInputValue(isSelectedEmoji ? selectedValue : '')
}, [isSelectedEmoji, selectedValue])
const selectTab = (type: IconPickerType | 'reset') => {
if (type === 'reset') {
onIconChange(undefined)
@@ -13,6 +13,9 @@ import { FOCUS_TAGS_INPUT_COMMAND, keyboardStringForShortcut } from '@standardno
import { useCommandService } from '../CommandProvider'
import { useItemLinks } from '@/Hooks/useItemLinks'
import RoundIconButton from '../Button/RoundIconButton'
import VaultNameBadge from '../Vaults/VaultNameBadge'
import LastEditedByBadge from '../Vaults/LastEditedByBadge'
import { useItemVaultInfo } from '@/Hooks/useItemVaultInfo'
type Props = {
linkingController: LinkingController
@@ -161,6 +164,8 @@ const LinkedItemBubblesContainer = ({
const shouldHideToggle = hideToggle || (!canShowContainerToggle && !isCollapsed)
const { vault, lastEditedByContact } = useItemVaultInfo(item)
if (readonly && itemsToDisplay.length === 0) {
return null
}
@@ -183,6 +188,8 @@ const LinkedItemBubblesContainer = ({
)}
ref={setLinkContainer}
>
{!!vault && <VaultNameBadge vault={vault} />}
{!!lastEditedByContact && <LastEditedByBadge contact={lastEditedByContact} />}
{visibleItems.map((link) => (
<LinkedItemBubble
link={link}
@@ -1,22 +1,42 @@
import { PaneLayout } from '@/Controllers/PaneController/PaneLayout'
import useIsTabletOrMobileScreen from '@/Hooks/useIsTabletOrMobileScreen'
import { classNames } from '@standardnotes/snjs'
import { StatusServiceEvent, classNames } from '@standardnotes/snjs'
import RoundIconButton from '../Button/RoundIconButton'
import { useResponsiveAppPane } from '../Panes/ResponsivePaneProvider'
import { useState, useEffect } from 'react'
import { useApplication } from '../ApplicationProvider'
import CountBubble from '../Preferences/PreferencesComponents/CountBubble'
/** This button is displayed in the items list header */
export const NavigationMenuButton = () => {
const application = useApplication()
const { setPaneLayout } = useResponsiveAppPane()
const { isTabletOrMobile } = useIsTabletOrMobileScreen()
const { isTabletOrMobile, isMobile } = useIsTabletOrMobileScreen()
const [bubbleCount, setBubbleCount] = useState<string | undefined>(() => {
return application.status.totalPreferencesBubbleCount
? application.status.totalPreferencesBubbleCount.toString()
: undefined
})
useEffect(() => {
return application.status.addEventObserver((event, message) => {
if (event !== StatusServiceEvent.PreferencesBubbleCountChanged) {
return
}
setBubbleCount(message)
})
}, [application.status])
return (
<RoundIconButton
className={classNames(isTabletOrMobile ? 'flex' : 'hidden', 'mr-3')}
onClick={() => {
setPaneLayout(PaneLayout.TagSelection)
}}
label="Open navigation menu"
icon="menu-variant"
/>
<div className={classNames(isTabletOrMobile ? 'flex' : 'hidden', 'relative h-10 w-10', 'mr-3')}>
<RoundIconButton
onClick={() => {
setPaneLayout(PaneLayout.TagSelection)
}}
label="Open navigation menu"
icon="menu-variant"
/>
{isMobile && bubbleCount && <CountBubble position="right" count={bubbleCount} />}
</div>
)
}
@@ -1,41 +1,24 @@
import { FunctionComponent } from 'react'
import Icon from '../Icon/Icon'
import { useApplication } from '../ApplicationProvider'
import { DecryptedItemInterface } from '@standardnotes/snjs'
import VaultNameBadge from '../Vaults/VaultNameBadge'
import LastEditedByBadge from '../Vaults/LastEditedByBadge'
import { useItemVaultInfo } from '@/Hooks/useItemVaultInfo'
type Props = {
item: DecryptedItemInterface
}
const CollaborationInfoHUD: FunctionComponent<Props> = ({ item }) => {
const application = useApplication()
const { vault, lastEditedByContact } = useItemVaultInfo(item)
if (!application.featuresController.isEntitledToVaults()) {
return null
}
if (application.items.isTemplateItem(item)) {
return null
}
const vault = application.vaults.getItemVault(item)
if (!vault) {
return null
}
const lastEditedBy = application.sharedVaults.getItemLastEditedBy(item)
return (
<div className="flex flex-wrap items-start gap-2">
<VaultNameBadge vault={vault} />
{lastEditedBy && (
<div title="Last edited by" className="flex select-none rounded bg-info px-1.5 py-1 text-info-contrast">
<Icon ariaLabel="Shared by" type="pencil" className="mr-1 text-info-contrast" size="medium" />
<span className="mr-auto overflow-hidden text-ellipsis text-xs">{lastEditedBy?.name}</span>
</div>
)}
{lastEditedByContact && <LastEditedByBadge contact={lastEditedByContact} />}
</div>
)
}
@@ -911,7 +911,6 @@ class NoteView extends AbstractComponent<NoteViewProps, State> {
)}
{renderHeaderOptions && (
<div className="note-view-options-buttons flex items-center gap-3">
<CollaborationInfoHUD item={this.note} />
<LinkedItemsButton
linkingController={this.application.linkingController}
onClickPreprocessing={this.ensureNoteIsInsertedBeforeUIAction}
@@ -931,6 +930,9 @@ class NoteView extends AbstractComponent<NoteViewProps, State> {
</div>
)}
</div>
<div className="mb-1 mt-2.5 md:hidden">
<CollaborationInfoHUD item={this.note} />
</div>
<div className="hidden md:block">
<LinkedItemBubblesContainer item={this.note} linkingController={this.application.linkingController} />
</div>
@@ -208,6 +208,8 @@ const NotesOptions = ({ notes, closeMenu }: NotesOptionsProps) => {
return <ProtectedUnauthorizedLabel />
}
const areSomeNotesInSharedVault = notes.some((note) => application.vaults.getItemVault(note)?.isSharedVaultListing())
if (notes.length === 0) {
return null
}
@@ -468,9 +470,13 @@ const NotesOptions = ({ notes, closeMenu }: NotesOptionsProps) => {
enableSuperMarkdownPreview={enableSuperMarkdownPreview}
/>
)}
<HorizontalSeparator classes="my-2" />
<ListedActionsOption iconClassName={iconClass} application={application} note={notes[0]} />
{!areSomeNotesInSharedVault && (
<>
<HorizontalSeparator classes="my-2" />
<ListedActionsOption iconClassName={iconClass} application={application} note={notes[0]} />
</>
)}
<HorizontalSeparator classes="my-2" />
@@ -1,4 +1,4 @@
import { FunctionComponent, useCallback, useEffect, useState } from 'react'
import { FunctionComponent, useState } from 'react'
import { observer } from 'mobx-react-lite'
import { Text, Title } from '@/Components/Preferences/PreferencesComponents/Content'
@@ -9,83 +9,15 @@ import { WebApplication } from '@/Application/WebApplication'
import { formatLastSyncDate } from '@/Utils/DateUtils'
import PreferencesGroup from '../../PreferencesComponents/PreferencesGroup'
import PreferencesSegment from '../../PreferencesComponents/PreferencesSegment'
import HorizontalSeparator from '@/Components/Shared/HorizontalSeparator'
import { featureTrunkTransitionEnabled } from '@/FeatureTrunk'
type Props = {
application: WebApplication
}
const Sync: FunctionComponent<Props> = ({ application }: Props) => {
const TRANSITION_STATUS_REFRESH_INTERVAL = 5000
const [isSyncingInProgress, setIsSyncingInProgress] = useState(false)
const [isTransitionInProgress, setIsTransitionInProgress] = useState(false)
const [showTransitionSegment, setShowTransitionSegment] = useState(false)
const [transitionStatus, setTransitionStatus] = useState('')
const [transitionStatusIntervalRef, setTransitionStatusIntervalRef] = useState<NodeJS.Timer | null>(null)
const [lastSyncDate, setLastSyncDate] = useState(formatLastSyncDate(application.sync.getLastSyncDate() as Date))
const setupTransitionStatusRefresh = useCallback(async () => {
const interval = setInterval(async () => {
const statusOrError = await application.getTransitionStatus.execute()
if (statusOrError.isFailed()) {
await application.alerts.alert(statusOrError.getError())
return
}
const status = statusOrError.getValue()
setTransitionStatus(status)
}, TRANSITION_STATUS_REFRESH_INTERVAL)
setTransitionStatusIntervalRef(interval)
}, [application, setTransitionStatus, setTransitionStatusIntervalRef])
useEffect(() => {
if (!featureTrunkTransitionEnabled()) {
return
}
async function checkTransitionStatus() {
const statusOrError = await application.getTransitionStatus.execute()
if (statusOrError.isFailed()) {
await application.alerts.alert(statusOrError.getError())
return
}
const status = statusOrError.getValue()
if (status === 'FINISHED') {
if (transitionStatusIntervalRef) {
clearInterval(transitionStatusIntervalRef)
}
setIsTransitionInProgress(false)
setTransitionStatus(status)
setShowTransitionSegment(false)
return
}
setShowTransitionSegment(true)
setTransitionStatus(status)
if (status === 'STARTED') {
setIsTransitionInProgress(true)
if (!transitionStatusIntervalRef) {
await setupTransitionStatusRefresh()
}
}
}
void checkTransitionStatus()
}, [
application,
setIsTransitionInProgress,
setTransitionStatus,
setShowTransitionSegment,
setupTransitionStatusRefresh,
transitionStatusIntervalRef,
])
const doSynchronization = async () => {
setIsSyncingInProgress(true)
@@ -101,19 +33,6 @@ const Sync: FunctionComponent<Props> = ({ application }: Props) => {
}
}
const doTransition = useCallback(async () => {
const resultOrError = await application.startTransition.execute()
if (resultOrError.isFailed()) {
await application.alerts.alert(resultOrError.getError())
return
}
setIsTransitionInProgress(true)
await setupTransitionStatusRefresh()
}, [application, setupTransitionStatusRefresh])
return (
<PreferencesGroup>
<PreferencesSegment>
@@ -132,35 +51,6 @@ const Sync: FunctionComponent<Props> = ({ application }: Props) => {
</div>
</div>
</PreferencesSegment>
{showTransitionSegment && (
<>
<HorizontalSeparator classes="my-4" />
<PreferencesSegment>
<div className="flex flex-row items-center">
<div className="flex flex-grow flex-col">
<Title>Transition Account</Title>
<Text>
Transition your account to our new infrastructure in order to enable new features and improve your
overall experience. Depending on the amount of data you have, this process may take a few moments.
</Text>
{isTransitionInProgress && (
<Text>
<span className="font-bold">Transition status:</span> {transitionStatus}
</Text>
)}
{!isTransitionInProgress && (
<Button
className="mt-3 min-w-20"
label="Start transition"
disabled={isTransitionInProgress}
onClick={doTransition}
/>
)}
</div>
</div>
</PreferencesSegment>
</>
)}
</PreferencesGroup>
)
}

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