Compare commits

...
Author SHA1 Message Date
StandardNotes CI 8553914d08 chore(release): publish
- @standardnotes/[email protected].1
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].0
 - @standardnotes/[email protected].2
 - @standardnotes/[email protected].2
 - @standardnotes/[email protected].5
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].2
 - @standardnotes/[email protected].0
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].0
2022-10-12 19:22:17 +00:00
Mo 18c821d8eb feat: replace private workspaces with private usernames (#1783) 2022-10-12 13:52:34 -05:00
StandardNotes CI 038e456c6a chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected].4
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].1
 - @standardnotes/[email protected].2
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].4
2022-10-12 17:11:49 +00:00
Aman Harwara 37abac2ec2 fix: bidirectional linking indicator for files 2022-10-12 22:01:42 +05:30
Aman Harwara 81532f2f20 refactor: item linking (#1781) 2022-10-12 21:57:51 +05:30
StandardNotes CI 2b89ad488f chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected].3
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].3
2022-10-12 15:57:21 +00:00
Aman Harwara 075d7f444d fix: css variable fallback 2022-10-12 20:58:02 +05:30
StandardNotes CI a2dc739686 chore(release): publish
- @standardnotes/[email protected].0
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].2
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].0
 - @standardnotes/[email protected].1
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].2
2022-10-12 12:41:26 +00:00
Karol Sójko fabce1f7ac feat(api): add keyshare initiation for workspaces 2022-10-12 14:05:11 +02:00
StandardNotes CI 3fbbacfc25 chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected].1
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].1
2022-10-11 19:52:04 +00:00
Aman Harwara 990939318f fix: viewport height-related issue on mobile 2022-10-12 00:54:15 +05:30
StandardNotes CI 46e3cae804 chore(release): publish
- @standardnotes/[email protected].1
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].1
 - @standardnotes/[email protected].1
 - @standardnotes/[email protected].1
 - @standardnotes/[email protected].0
 - @standardnotes/[email protected].0
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].0
 - @standardnotes/[email protected].0
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].0
2022-10-11 18:53:18 +00:00
Aman Harwara e3f28421ff feat: item linking (#1779) 2022-10-11 23:54:00 +05:30
StandardNotes CI d22c164e5d chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected]
2022-10-11 14:53:46 +00:00
Mo 5cf37037b7 chore(mobile): podfile.lock 2022-10-11 09:23:31 -05:00
Mo 9fc77d861e fix(mobile): issue with black screen on iOS (#1777) 2022-10-11 09:08:34 -05:00
StandardNotes CI b80b04d374 chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected].9
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].8
2022-10-11 13:04:23 +00:00
Mo b82eac09a8 fix: package import location 2022-10-11 07:30:49 -05:00
StandardNotes CI 7c9f0d3143 chore(release): publish
- @standardnotes/[email protected].0
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].0
 - @standardnotes/[email protected].0
 - @standardnotes/[email protected].0
 - @standardnotes/[email protected].0
 - @standardnotes/[email protected].8
 - @standardnotes/[email protected].0
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].0
 - @standardnotes/[email protected].0
 - @standardnotes/[email protected].0
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].0
 - @standardnotes/[email protected].7
2022-10-11 10:54:24 +00:00
Karol Sójko fb41f651ec feat(api): add listing workspace users 2022-10-11 12:21:06 +02:00
StandardNotes CI 7a43e2d368 chore(release): publish
- @standardnotes/[email protected].0
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].7
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].0
 - @standardnotes/[email protected].4
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].6
2022-10-11 10:20:35 +00:00
Karol Sójko 49b3d2714f feat(api): add access level to invitation requests 2022-10-11 11:46:27 +02:00
StandardNotes CI 1540143dad chore(release): publish
- @standardnotes/[email protected].0
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].3
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].6
 - @standardnotes/[email protected].0
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].0
 - @standardnotes/[email protected].3
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].5
2022-10-11 09:26:07 +00:00
Karol Sójko 8376381981 feat(api): add listing workspaces 2022-10-11 10:57:24 +02:00
Karol Sójko 15e2c82e65 feat(api): add accepting invites to workspace 2022-10-11 10:33:23 +02:00
StandardNotes CI 1c22c1ab0c chore(release): publish
- @standardnotes/[email protected].1
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].2
 - @standardnotes/[email protected].4
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].5
 - @standardnotes/[email protected].2
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].6
 - @standardnotes/[email protected].1
 - @standardnotes/[email protected].2
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].4
2022-10-10 16:50:35 +00:00
Aman Harwara 27d2c95b5b fix: dark mode not working in editors (#1773) 2022-10-10 21:47:57 +05:30
StandardNotes CI dcb8024deb chore(release): publish
- @standardnotes/[email protected].0
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].1
 - @standardnotes/[email protected].3
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].4
 - @standardnotes/[email protected].1
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].5
 - @standardnotes/[email protected].0
 - @standardnotes/[email protected].1
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].3
2022-10-10 11:26:23 +00:00
Karol Sójko 85d2415625 fix(api): specs 2022-10-10 12:54:22 +02:00
Karol Sójko e2e7605414 fix: linter issues 2022-10-10 12:24:03 +02:00
Karol Sójko 158ca6ac6a feat(api): add inviting to workspace 2022-10-10 11:57:44 +02:00
Karol Sójko a275a45753 fix(api): workspace creation arguments 2022-10-10 10:40:46 +02:00
StandardNotes CI 8863ee82bb chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected].3
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].2
2022-10-09 15:47:24 +00:00
Aman Harwara 6a4b5c5cdc fix: move viewport early return above 2022-10-09 20:39:48 +05:30
Aman Harwara cd1669f56f fix: potential fix for viewport height issue (#1772) 2022-10-09 20:37:44 +05:30
StandardNotes CI 037007f0ec chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected].2
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].1
2022-10-08 08:12:13 +00:00
Aman Harwara e7ca12babe fix: open upgrade page in new tab if user has account 2022-10-08 13:12:32 +05:30
StandardNotes CI c4c33db1a4 chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected].1
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].0
2022-10-07 20:41:32 +00:00
Aman Harwara 734b986294 feat: open purchase flow when clicking upgrade cta with no account (#1771) 2022-10-08 01:39:40 +05:30
Aman Harwara 59db63f052 feat: change quick settings menu layout (#1770) 2022-10-08 01:08:54 +05:30
Mo 20226c3269 fix(desktop): use different method to send messages across main and render without node integration in preload (#1769) 2022-10-07 14:30:07 -05:00
StandardNotes CI a1352d9f65 chore(release): publish
- @standardnotes/[email protected].0
 - @standardnotes/[email protected]
2022-10-07 13:57:12 +00:00
Mo 7d21046b51 feat(mobile): delete account option in settings (#1768) 2022-10-07 08:23:18 -05:00
Mo a365c17c46 fix(mobile): disable landscape for iPhone 2022-10-07 07:27:37 -05:00
StandardNotes CI c1981334e1 chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected].2
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].5
2022-10-07 10:51:03 +00:00
Aman Harwara 1e3acd50e9 fix: add fallback for viewport height on mobile (#1766) 2022-10-07 15:41:45 +05:30
196 changed files with 4247 additions and 1812 deletions
+49
View File
@@ -3,6 +3,55 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.16.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-12)
**Note:** Version bump only for package @standardnotes/api
# [1.16.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-12)
### Features
* **api:** add keyshare initiation for workspaces ([fabce1f](https://github.com/standardnotes/app/commit/fabce1f7aca5c9caaa5bb3908b2dc68063c690d0))
## [1.15.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-11)
**Note:** Version bump only for package @standardnotes/api
# [1.15.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-11)
### Features
* **api:** add listing workspace users ([fb41f65](https://github.com/standardnotes/app/commit/fb41f651eca45dd62314609c2f389d5dc683c36e))
# [1.14.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-11)
### Features
* **api:** add access level to invitation requests ([49b3d27](https://github.com/standardnotes/app/commit/49b3d2714fbe4ac9f9242097b8df6b4e3d604513))
# [1.13.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-11)
### Features
* **api:** add accepting invites to workspace ([15e2c82](https://github.com/standardnotes/app/commit/15e2c82e654ce3f983cd42b01f14d18435b3e939))
* **api:** add listing workspaces ([8376381](https://github.com/standardnotes/app/commit/837638198112df24c30e53803a7ddf5f841975b0))
## [1.12.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-10)
**Note:** Version bump only for package @standardnotes/api
# [1.12.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-10)
### Bug Fixes
* **api:** specs ([85d2415](https://github.com/standardnotes/app/commit/85d24156254015914c684f900c77c080caa52eb7))
* **api:** workspace creation arguments ([a275a45](https://github.com/standardnotes/app/commit/a275a45753abc4a29a89de5fe784260165297bbe))
* linter issues ([e2e7605](https://github.com/standardnotes/app/commit/e2e76054142e8d67cac69280e2a3efcb15cf67f1))
### Features
* **api:** add inviting to workspace ([158ca6a](https://github.com/standardnotes/app/commit/158ca6ac6a428e082c8f984791f80fcaadcb3e34))
# [1.11.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-07)
### Features
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/api",
"version": "1.11.0",
"version": "1.16.1",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
@@ -36,7 +36,7 @@
"typescript": "*"
},
"dependencies": {
"@standardnotes/common": "^1.32.0",
"@standardnotes/common": "^1.39.0",
"@standardnotes/encryption": "workspace:*",
"@standardnotes/models": "workspace:*",
"@standardnotes/responses": "workspace:*",
@@ -1,3 +1,8 @@
export enum WorkspaceApiOperations {
Creating,
Inviting,
Accepting,
ListingWorkspaces,
ListingWorkspaceUsers,
InitiatingKeyshare,
}
@@ -1,5 +1,13 @@
import { WorkspaceAccessLevel, WorkspaceType } from '@standardnotes/common'
import { HttpStatusCode } from '../../Http'
import { WorkspaceCreationResponse } from '../../Response/Workspace/WorkspaceCreationResponse'
import { WorkspaceInvitationAcceptingResponse } from '../../Response/Workspace/WorkspaceInvitationAcceptingResponse'
import { WorkspaceInvitationResponse } from '../../Response/Workspace/WorkspaceInvitationResponse'
import { WorkspaceListResponse } from '../../Response/Workspace/WorkspaceListResponse'
import { WorkspaceUserListResponse } from '../../Response/Workspace/WorkspaceUserListResponse'
import { WorkspaceServerInterface } from '../../Server/Workspace/WorkspaceServerInterface'
import { WorkspaceKeyshareInitiatingResponse } from '../../Response/Workspace/WorkspaceKeyshareInitiatingResponse'
import { WorkspaceApiOperations } from './WorkspaceApiOperations'
import { WorkspaceApiService } from './WorkspaceApiService'
@@ -14,10 +22,29 @@ describe('WorkspaceApiService', () => {
workspaceServer.createWorkspace = jest.fn().mockReturnValue({
data: { uuid: '1-2-3' },
} as jest.Mocked<WorkspaceCreationResponse>)
workspaceServer.inviteToWorkspace = jest.fn().mockReturnValue({
data: { uuid: 'i-1-2-3' },
} as jest.Mocked<WorkspaceInvitationResponse>)
workspaceServer.acceptInvite = jest.fn().mockReturnValue({
data: { success: true },
} as jest.Mocked<WorkspaceInvitationAcceptingResponse>)
workspaceServer.listWorkspaces = jest.fn().mockReturnValue({
status: HttpStatusCode.Success,
data: { ownedWorkspaces: [], joinedWorkspaces: [] },
} as jest.Mocked<WorkspaceListResponse>)
workspaceServer.listWorkspaceUsers = jest.fn().mockReturnValue({
status: HttpStatusCode.Success,
data: { users: [] },
} as jest.Mocked<WorkspaceUserListResponse>)
workspaceServer.initiateKeyshare = jest.fn().mockReturnValue({
status: HttpStatusCode.Success,
data: { success: true },
} as jest.Mocked<WorkspaceKeyshareInitiatingResponse>)
})
it('should create a workspace', async () => {
const response = await createService().createWorkspace({
workspaceType: WorkspaceType.Private,
encryptedPrivateKey: 'foo',
encryptedWorkspaceKey: 'bar',
publicKey: 'buzz',
@@ -32,6 +59,7 @@ describe('WorkspaceApiService', () => {
encryptedPrivateKey: 'foo',
encryptedWorkspaceKey: 'bar',
publicKey: 'buzz',
workspaceType: 'private',
})
})
@@ -44,6 +72,7 @@ describe('WorkspaceApiService', () => {
let error = null
try {
await service.createWorkspace({
workspaceType: WorkspaceType.Private,
encryptedPrivateKey: 'foo',
encryptedWorkspaceKey: 'bar',
publicKey: 'buzz',
@@ -63,6 +92,7 @@ describe('WorkspaceApiService', () => {
let error = null
try {
await createService().createWorkspace({
workspaceType: WorkspaceType.Private,
encryptedPrivateKey: 'foo',
encryptedWorkspaceKey: 'bar',
publicKey: 'buzz',
@@ -73,4 +103,266 @@ describe('WorkspaceApiService', () => {
expect(error).not.toBeNull()
})
it('should invite to a workspace', async () => {
const response = await createService().inviteToWorkspace({
workspaceUuid: 'w-1-2-3',
inviteeEmail: '[email protected]',
accessLevel: WorkspaceAccessLevel.WriteAndRead,
})
expect(response).toEqual({
data: {
uuid: 'i-1-2-3',
},
})
expect(workspaceServer.inviteToWorkspace).toHaveBeenCalledWith({
workspaceUuid: 'w-1-2-3',
inviteeEmail: '[email protected]',
accessLevel: 'write-and-read',
})
})
it('should not invite to a workspace if it is already inviting', async () => {
const service = createService()
Object.defineProperty(service, 'operationsInProgress', {
get: () => new Map([[WorkspaceApiOperations.Inviting, true]]),
})
let error = null
try {
await service.inviteToWorkspace({
workspaceUuid: 'w-1-2-3',
inviteeEmail: '[email protected]',
accessLevel: WorkspaceAccessLevel.WriteAndRead,
})
} catch (caughtError) {
error = caughtError
}
expect(error).not.toBeNull()
})
it('should not invite to a workspace if the server fails', async () => {
workspaceServer.inviteToWorkspace = jest.fn().mockImplementation(() => {
throw new Error('Oops')
})
let error = null
try {
await createService().inviteToWorkspace({
workspaceUuid: 'w-1-2-3',
inviteeEmail: '[email protected]',
accessLevel: WorkspaceAccessLevel.WriteAndRead,
})
} catch (caughtError) {
error = caughtError
}
expect(error).not.toBeNull()
})
it('should accept invite to a workspace', async () => {
const response = await createService().acceptInvite({
userUuid: 'u-1-2-3',
inviteUuid: 'i-1-2-3',
publicKey: 'foo',
encryptedPrivateKey: 'bar',
})
expect(response).toEqual({
data: {
success: true,
},
})
expect(workspaceServer.acceptInvite).toHaveBeenCalledWith({
userUuid: 'u-1-2-3',
inviteUuid: 'i-1-2-3',
publicKey: 'foo',
encryptedPrivateKey: 'bar',
})
})
it('should not accept invite to a workspace if it is already accepting', async () => {
const service = createService()
Object.defineProperty(service, 'operationsInProgress', {
get: () => new Map([[WorkspaceApiOperations.Accepting, true]]),
})
let error = null
try {
await service.acceptInvite({
userUuid: 'u-1-2-3',
inviteUuid: 'i-1-2-3',
publicKey: 'foo',
encryptedPrivateKey: 'bar',
})
} catch (caughtError) {
error = caughtError
}
expect(error).not.toBeNull()
})
it('should not accept invite to a workspace if the server fails', async () => {
workspaceServer.acceptInvite = jest.fn().mockImplementation(() => {
throw new Error('Oops')
})
let error = null
try {
await createService().acceptInvite({
userUuid: 'u-1-2-3',
inviteUuid: 'i-1-2-3',
publicKey: 'foo',
encryptedPrivateKey: 'bar',
})
} catch (caughtError) {
error = caughtError
}
expect(error).not.toBeNull()
})
it('should list workspaces', async () => {
const response = await createService().listWorkspaces()
expect(response).toEqual({
status: 200,
data: {
ownedWorkspaces: [],
joinedWorkspaces: [],
},
})
expect(workspaceServer.listWorkspaces).toHaveBeenCalled()
})
it('should not list workspaces if it is already listing them', async () => {
const service = createService()
Object.defineProperty(service, 'operationsInProgress', {
get: () => new Map([[WorkspaceApiOperations.ListingWorkspaces, true]]),
})
let error = null
try {
await service.listWorkspaces()
} catch (caughtError) {
error = caughtError
}
expect(error).not.toBeNull()
})
it('should not list workspaces if the server fails', async () => {
workspaceServer.listWorkspaces = jest.fn().mockImplementation(() => {
throw new Error('Oops')
})
let error = null
try {
await createService().listWorkspaces()
} catch (caughtError) {
error = caughtError
}
expect(error).not.toBeNull()
})
it('should list workspace users', async () => {
const response = await createService().listWorkspaceUsers({ workspaceUuid: 'w-1-2-3' })
expect(response).toEqual({
status: 200,
data: {
users: [],
},
})
expect(workspaceServer.listWorkspaceUsers).toHaveBeenCalledWith({ workspaceUuid: 'w-1-2-3' })
})
it('should not list workspace users if it is already listing them', async () => {
const service = createService()
Object.defineProperty(service, 'operationsInProgress', {
get: () => new Map([[WorkspaceApiOperations.ListingWorkspaceUsers, true]]),
})
let error = null
try {
await service.listWorkspaceUsers({ workspaceUuid: 'w-1-2-3' })
} catch (caughtError) {
error = caughtError
}
expect(error).not.toBeNull()
})
it('should not list workspace users if the server fails', async () => {
workspaceServer.listWorkspaceUsers = jest.fn().mockImplementation(() => {
throw new Error('Oops')
})
let error = null
try {
await createService().listWorkspaceUsers({ workspaceUuid: 'w-1-2-3' })
} catch (caughtError) {
error = caughtError
}
expect(error).not.toBeNull()
})
it('should initiate keyshare in workspace for user', async () => {
const response = await createService().initiateKeyshare({
workspaceUuid: 'w-1-2-3',
userUuid: 'u-1-2-3',
encryptedWorkspaceKey: 'foobar',
})
expect(response).toEqual({
status: 200,
data: {
success: true,
},
})
expect(workspaceServer.initiateKeyshare).toHaveBeenCalledWith({
workspaceUuid: 'w-1-2-3',
userUuid: 'u-1-2-3',
encryptedWorkspaceKey: 'foobar',
})
})
it('should not initiate keyshare in workspace if it is already initiating', async () => {
const service = createService()
Object.defineProperty(service, 'operationsInProgress', {
get: () => new Map([[WorkspaceApiOperations.InitiatingKeyshare, true]]),
})
let error = null
try {
await service.initiateKeyshare({ workspaceUuid: 'w-1-2-3', userUuid: 'u-1-2-3', encryptedWorkspaceKey: 'foobar' })
} catch (caughtError) {
error = caughtError
}
expect(error).not.toBeNull()
})
it('should not initiate keyshare in workspace if the server fails', async () => {
workspaceServer.initiateKeyshare = jest.fn().mockImplementation(() => {
throw new Error('Oops')
})
let error = null
try {
await createService().initiateKeyshare({
workspaceUuid: 'w-1-2-3',
userUuid: 'u-1-2-3',
encryptedWorkspaceKey: 'foobar',
})
} catch (caughtError) {
error = caughtError
}
expect(error).not.toBeNull()
})
})
@@ -1,7 +1,14 @@
import { Uuid, WorkspaceAccessLevel, WorkspaceType } from '@standardnotes/common'
import { ErrorMessage } from '../../Error/ErrorMessage'
import { ApiCallError } from '../../Error/ApiCallError'
import { WorkspaceCreationResponse } from '../../Response/Workspace/WorkspaceCreationResponse'
import { WorkspaceInvitationAcceptingResponse } from '../../Response/Workspace/WorkspaceInvitationAcceptingResponse'
import { WorkspaceInvitationResponse } from '../../Response/Workspace/WorkspaceInvitationResponse'
import { WorkspaceServerInterface } from '../../Server/Workspace/WorkspaceServerInterface'
import { WorkspaceListResponse } from '../../Response/Workspace/WorkspaceListResponse'
import { WorkspaceUserListResponse } from '../../Response/Workspace/WorkspaceUserListResponse'
import { WorkspaceKeyshareInitiatingResponse } from '../../Response/Workspace/WorkspaceKeyshareInitiatingResponse'
import { WorkspaceApiServiceInterface } from './WorkspaceApiServiceInterface'
import { WorkspaceApiOperations } from './WorkspaceApiOperations'
@@ -13,31 +20,137 @@ export class WorkspaceApiService implements WorkspaceApiServiceInterface {
this.operationsInProgress = new Map()
}
async createWorkspace(dto: {
async initiateKeyshare(dto: {
workspaceUuid: string
userUuid: string
encryptedWorkspaceKey: string
encryptedPrivateKey: string
publicKey: string
workspaceName?: string
}): Promise<WorkspaceCreationResponse> {
if (this.operationsInProgress.get(WorkspaceApiOperations.Creating)) {
throw new ApiCallError(ErrorMessage.GenericInProgress)
}
this.operationsInProgress.set(WorkspaceApiOperations.Creating, true)
}): Promise<WorkspaceKeyshareInitiatingResponse> {
this.lockOperation(WorkspaceApiOperations.InitiatingKeyshare)
try {
const response = await this.workspaceServer.createWorkspace({
encryptedPrivateKey: dto.encryptedPrivateKey,
const response = await this.workspaceServer.initiateKeyshare({
workspaceUuid: dto.workspaceUuid,
userUuid: dto.userUuid,
encryptedWorkspaceKey: dto.encryptedWorkspaceKey,
publicKey: dto.publicKey,
workspaceName: dto.workspaceName,
})
this.operationsInProgress.set(WorkspaceApiOperations.Creating, false)
this.unlockOperation(WorkspaceApiOperations.InitiatingKeyshare)
return response
} catch (error) {
throw new ApiCallError(ErrorMessage.GenericFail)
}
}
async listWorkspaceUsers(dto: { workspaceUuid: string }): Promise<WorkspaceUserListResponse> {
this.lockOperation(WorkspaceApiOperations.ListingWorkspaceUsers)
try {
const response = await this.workspaceServer.listWorkspaceUsers({ workspaceUuid: dto.workspaceUuid })
this.unlockOperation(WorkspaceApiOperations.ListingWorkspaceUsers)
return response
} catch (error) {
throw new ApiCallError(ErrorMessage.GenericFail)
}
}
async listWorkspaces(): Promise<WorkspaceListResponse> {
this.lockOperation(WorkspaceApiOperations.ListingWorkspaces)
try {
const response = await this.workspaceServer.listWorkspaces({})
this.unlockOperation(WorkspaceApiOperations.ListingWorkspaces)
return response
} catch (error) {
throw new ApiCallError(ErrorMessage.GenericFail)
}
}
async acceptInvite(dto: {
inviteUuid: string
userUuid: string
publicKey: string
encryptedPrivateKey: string
}): Promise<WorkspaceInvitationAcceptingResponse> {
this.lockOperation(WorkspaceApiOperations.Accepting)
try {
const response = await this.workspaceServer.acceptInvite({
encryptedPrivateKey: dto.encryptedPrivateKey,
publicKey: dto.publicKey,
inviteUuid: dto.inviteUuid,
userUuid: dto.userUuid,
})
this.unlockOperation(WorkspaceApiOperations.Accepting)
return response
} catch (error) {
throw new ApiCallError(ErrorMessage.GenericFail)
}
}
async inviteToWorkspace(dto: {
inviteeEmail: string
workspaceUuid: Uuid
accessLevel: WorkspaceAccessLevel
}): Promise<WorkspaceInvitationResponse> {
this.lockOperation(WorkspaceApiOperations.Inviting)
try {
const response = await this.workspaceServer.inviteToWorkspace({
inviteeEmail: dto.inviteeEmail,
workspaceUuid: dto.workspaceUuid,
accessLevel: dto.accessLevel,
})
this.unlockOperation(WorkspaceApiOperations.Inviting)
return response
} catch (error) {
throw new ApiCallError(ErrorMessage.GenericFail)
}
}
async createWorkspace(dto: {
workspaceType: WorkspaceType
encryptedWorkspaceKey?: string
encryptedPrivateKey?: string
publicKey?: string
workspaceName?: string
}): Promise<WorkspaceCreationResponse> {
this.lockOperation(WorkspaceApiOperations.Creating)
try {
const response = await this.workspaceServer.createWorkspace({
workspaceType: dto.workspaceType,
encryptedPrivateKey: dto.encryptedPrivateKey,
encryptedWorkspaceKey: dto.encryptedWorkspaceKey,
publicKey: dto.publicKey,
workspaceName: dto.workspaceName,
})
this.unlockOperation(WorkspaceApiOperations.Creating)
return response
} catch (error) {
throw new ApiCallError(ErrorMessage.GenericFail)
}
}
private lockOperation(operation: WorkspaceApiOperations): void {
if (this.operationsInProgress.get(operation)) {
throw new ApiCallError(ErrorMessage.GenericInProgress)
}
this.operationsInProgress.set(operation, true)
}
private unlockOperation(operation: WorkspaceApiOperations): void {
this.operationsInProgress.set(operation, false)
}
}
@@ -1,10 +1,36 @@
import { WorkspaceCreationResponse } from '../../Response'
import { Uuid, WorkspaceAccessLevel, WorkspaceType } from '@standardnotes/common'
import { WorkspaceKeyshareInitiatingResponse } from '../../Response/Workspace/WorkspaceKeyshareInitiatingResponse'
import { WorkspaceCreationResponse } from '../../Response/Workspace/WorkspaceCreationResponse'
import { WorkspaceInvitationAcceptingResponse } from '../../Response/Workspace/WorkspaceInvitationAcceptingResponse'
import { WorkspaceInvitationResponse } from '../../Response/Workspace/WorkspaceInvitationResponse'
import { WorkspaceListResponse } from '../../Response/Workspace/WorkspaceListResponse'
import { WorkspaceUserListResponse } from '../../Response/Workspace/WorkspaceUserListResponse'
export interface WorkspaceApiServiceInterface {
createWorkspace(dto: {
encryptedWorkspaceKey: string
encryptedPrivateKey: string
publicKey: string
workspaceType: WorkspaceType
encryptedWorkspaceKey?: string
encryptedPrivateKey?: string
publicKey?: string
workspaceName?: string
}): Promise<WorkspaceCreationResponse>
inviteToWorkspace(dto: {
inviteeEmail: string
workspaceUuid: Uuid
accessLevel: WorkspaceAccessLevel
}): Promise<WorkspaceInvitationResponse>
acceptInvite(dto: {
inviteUuid: Uuid
userUuid: Uuid
publicKey: string
encryptedPrivateKey: string
}): Promise<WorkspaceInvitationAcceptingResponse>
listWorkspaces(): Promise<WorkspaceListResponse>
listWorkspaceUsers(dto: { workspaceUuid: Uuid }): Promise<WorkspaceUserListResponse>
initiateKeyshare(dto: {
workspaceUuid: Uuid
userUuid: Uuid
encryptedWorkspaceKey: string
}): Promise<WorkspaceKeyshareInitiatingResponse>
}
@@ -1,7 +1,10 @@
import { WorkspaceType } from '@standardnotes/common'
export type WorkspaceCreationRequestParams = {
encryptedWorkspaceKey: string
encryptedPrivateKey: string
publicKey: string
workspaceType: WorkspaceType
encryptedWorkspaceKey?: string
encryptedPrivateKey?: string
publicKey?: string
workspaceName?: string
[additionalParam: string]: unknown
}
@@ -0,0 +1,9 @@
import { Uuid } from '@standardnotes/common'
export type WorkspaceInvitationAcceptingRequestParams = {
inviteUuid: Uuid
userUuid: Uuid
publicKey: string
encryptedPrivateKey: string
[additionalParam: string]: unknown
}
@@ -0,0 +1,8 @@
import { Uuid, WorkspaceAccessLevel } from '@standardnotes/common'
export type WorkspaceInvitationRequestParams = {
workspaceUuid: Uuid
inviteeEmail: string
accessLevel: WorkspaceAccessLevel
[additionalParam: string]: unknown
}
@@ -0,0 +1,8 @@
import { Uuid } from '@standardnotes/common'
export type WorkspaceKeyshareInitiatingRequestParams = {
userUuid: Uuid
workspaceUuid: Uuid
encryptedWorkspaceKey: string
[additionalParam: string]: unknown
}
@@ -0,0 +1,3 @@
export type WorkspaceListRequestParams = {
[additionalParam: string]: unknown
}
@@ -0,0 +1,6 @@
import { Uuid } from '@standardnotes/common'
export type WorkspaceUserListRequestParams = {
workspaceUuid: Uuid
[additionalParam: string]: unknown
}
+5
View File
@@ -7,3 +7,8 @@ export * from './Subscription/SubscriptionInviteRequestParams'
export * from './User/UserRegistrationRequestParams'
export * from './WebSocket/WebSocketConnectionTokenRequestParams'
export * from './Workspace/WorkspaceCreationRequestParams'
export * from './Workspace/WorkspaceInvitationAcceptingRequestParams'
export * from './Workspace/WorkspaceInvitationRequestParams'
export * from './Workspace/WorkspaceKeyshareInitiatingRequestParams'
export * from './Workspace/WorkspaceListRequestParams'
export * from './Workspace/WorkspaceUserListRequestParams'
@@ -0,0 +1,9 @@
import { Either } from '@standardnotes/common'
import { HttpErrorResponseBody } from '../../Http/HttpErrorResponseBody'
import { HttpResponse } from '../../Http/HttpResponse'
import { WorkspaceInvitationAcceptingResponseBody } from './WorkspaceInvitationAcceptingResponseBody'
export interface WorkspaceInvitationAcceptingResponse extends HttpResponse {
data: Either<WorkspaceInvitationAcceptingResponseBody, HttpErrorResponseBody>
}
@@ -0,0 +1,3 @@
export type WorkspaceInvitationAcceptingResponseBody = {
success: boolean
}
@@ -0,0 +1,9 @@
import { Either } from '@standardnotes/common'
import { HttpErrorResponseBody } from '../../Http/HttpErrorResponseBody'
import { HttpResponse } from '../../Http/HttpResponse'
import { WorkspaceInvitationResponseBody } from './WorkspaceInvitationResponseBody'
export interface WorkspaceInvitationResponse extends HttpResponse {
data: Either<WorkspaceInvitationResponseBody, HttpErrorResponseBody>
}
@@ -0,0 +1,3 @@
export type WorkspaceInvitationResponseBody = {
uuid: string
}
@@ -0,0 +1,9 @@
import { Either } from '@standardnotes/common'
import { HttpErrorResponseBody } from '../../Http/HttpErrorResponseBody'
import { HttpResponse } from '../../Http/HttpResponse'
import { WorkspaceKeyshareInitiatingResponseBody } from './WorkspaceKeyshareInitiatingResponseBody'
export interface WorkspaceKeyshareInitiatingResponse extends HttpResponse {
data: Either<WorkspaceKeyshareInitiatingResponseBody, HttpErrorResponseBody>
}
@@ -0,0 +1,3 @@
export type WorkspaceKeyshareInitiatingResponseBody = {
success: boolean
}
@@ -0,0 +1,9 @@
import { Either } from '@standardnotes/common'
import { HttpErrorResponseBody } from '../../Http/HttpErrorResponseBody'
import { HttpResponse } from '../../Http/HttpResponse'
import { WorkspaceListResponseBody } from './WorkspaceListResponseBody'
export interface WorkspaceListResponse extends HttpResponse {
data: Either<WorkspaceListResponseBody, HttpErrorResponseBody>
}
@@ -0,0 +1,6 @@
import { Workspace } from '@standardnotes/models'
export type WorkspaceListResponseBody = {
ownedWorkspaces: Array<Workspace>
joinedWorkspaces: Array<Workspace>
}
@@ -0,0 +1,9 @@
import { Either } from '@standardnotes/common'
import { HttpErrorResponseBody } from '../../Http/HttpErrorResponseBody'
import { HttpResponse } from '../../Http/HttpResponse'
import { WorkspaceUserListResponseBody } from './WorkspaceUserListResponseBody'
export interface WorkspaceUserListResponse extends HttpResponse {
data: Either<WorkspaceUserListResponseBody, HttpErrorResponseBody>
}
@@ -0,0 +1,5 @@
import { WorkspaceUser } from '@standardnotes/models'
export type WorkspaceUserListResponseBody = {
users: Array<WorkspaceUser>
}
+10
View File
@@ -14,3 +14,13 @@ export * from './WebSocket/WebSocketConnectionTokenResponse'
export * from './WebSocket/WebSocketConnectionTokenResponseBody'
export * from './Workspace/WorkspaceCreationResponse'
export * from './Workspace/WorkspaceCreationResponseBody'
export * from './Workspace/WorkspaceInvitationAcceptingResponse'
export * from './Workspace/WorkspaceInvitationAcceptingResponseBody'
export * from './Workspace/WorkspaceKeyshareInitiatingResponse'
export * from './Workspace/WorkspaceKeyshareInitiatingResponseBody'
export * from './Workspace/WorkspaceInvitationResponse'
export * from './Workspace/WorkspaceInvitationResponseBody'
export * from './Workspace/WorkspaceListResponse'
export * from './Workspace/WorkspaceListResponseBody'
export * from './Workspace/WorkspaceUserListResponse'
export * from './Workspace/WorkspaceUserListResponseBody'
@@ -1,5 +1,13 @@
import { Uuid } from '@standardnotes/common'
const WorkspacePaths = {
createWorkspace: '/v1/workspaces',
listWorkspaces: '/v1/workspaces',
listWorkspaceUsers: (uuid: Uuid) => `/v1/workspaces/${uuid}/users`,
initiateKeyshare: (worksapceUuid: Uuid, userUuid: Uuid) =>
`/v1/workspaces/${worksapceUuid}/users/${userUuid}/keyshare`,
inviteToWorkspace: (uuid: Uuid) => `/v1/workspaces/${uuid}/invites`,
acceptInvite: (uuid: Uuid) => `/v1/invites/${uuid}/accept`,
}
export const Paths = {
@@ -1,5 +1,12 @@
import { HttpServiceInterface } from '../../Http'
import { WorkspaceAccessLevel, WorkspaceType } from '@standardnotes/common'
import { HttpServiceInterface, HttpStatusCode } from '../../Http'
import { WorkspaceCreationResponse } from '../../Response/Workspace/WorkspaceCreationResponse'
import { WorkspaceInvitationAcceptingResponse } from '../../Response/Workspace/WorkspaceInvitationAcceptingResponse'
import { WorkspaceInvitationResponse } from '../../Response/Workspace/WorkspaceInvitationResponse'
import { WorkspaceKeyshareInitiatingResponse } from '../../Response/Workspace/WorkspaceKeyshareInitiatingResponse'
import { WorkspaceListResponse } from '../../Response/Workspace/WorkspaceListResponse'
import { WorkspaceUserListResponse } from '../../Response/Workspace/WorkspaceUserListResponse'
import { WorkspaceServer } from './WorkspaceServer'
@@ -17,6 +24,7 @@ describe('WorkspaceServer', () => {
it('should create a workspace', async () => {
const response = await createServer().createWorkspace({
workspaceType: WorkspaceType.Private,
encryptedPrivateKey: 'foo',
encryptedWorkspaceKey: 'bar',
publicKey: 'buzz',
@@ -28,4 +36,89 @@ describe('WorkspaceServer', () => {
},
})
})
it('should inivte to a workspace', async () => {
httpService.post = jest.fn().mockReturnValue({
data: { uuid: 'i-1-2-3' },
} as jest.Mocked<WorkspaceInvitationResponse>)
const response = await createServer().inviteToWorkspace({
inviteeEmail: '[email protected]',
workspaceUuid: 'w-1-2-3',
accessLevel: WorkspaceAccessLevel.WriteAndRead,
})
expect(response).toEqual({
data: {
uuid: 'i-1-2-3',
},
})
})
it('should accept invitation to a workspace', async () => {
httpService.post = jest.fn().mockReturnValue({
data: { success: true },
} as jest.Mocked<WorkspaceInvitationAcceptingResponse>)
const response = await createServer().acceptInvite({
encryptedPrivateKey: 'foo',
inviteUuid: 'i-1-2-3',
publicKey: 'bar',
userUuid: 'u-1-2-3',
})
expect(response).toEqual({
data: {
success: true,
},
})
})
it('should list workspaces', async () => {
httpService.get = jest.fn().mockReturnValue({
status: HttpStatusCode.Success,
data: { ownedWorkspaces: [], joinedWorkspaces: [] },
} as jest.Mocked<WorkspaceListResponse>)
const response = await createServer().listWorkspaces({})
expect(response).toEqual({
status: 200,
data: { ownedWorkspaces: [], joinedWorkspaces: [] },
})
})
it('should list workspace users', async () => {
httpService.get = jest.fn().mockReturnValue({
status: HttpStatusCode.Success,
data: { users: [] },
} as jest.Mocked<WorkspaceUserListResponse>)
const response = await createServer().listWorkspaceUsers({ workspaceUuid: 'w-1-2-3' })
expect(response).toEqual({
status: 200,
data: { users: [] },
})
})
it('should initiate keyshare for user in a workspace', async () => {
httpService.post = jest.fn().mockReturnValue({
status: HttpStatusCode.Success,
data: { success: true },
} as jest.Mocked<WorkspaceKeyshareInitiatingResponse>)
const response = await createServer().initiateKeyshare({
workspaceUuid: 'w-1-2-3',
userUuid: 'u-1-2-3',
encryptedWorkspaceKey: 'foobar',
})
expect(response).toEqual({
status: 200,
data: {
success: true,
},
})
})
})
@@ -1,6 +1,16 @@
import { HttpServiceInterface } from '../../Http/HttpServiceInterface'
import { WorkspaceInvitationRequestParams } from '../../Request/Workspace/WorkspaceInvitationRequestParams'
import { WorkspaceCreationRequestParams } from '../../Request/Workspace/WorkspaceCreationRequestParams'
import { WorkspaceInvitationResponse } from '../../Response/Workspace/WorkspaceInvitationResponse'
import { WorkspaceCreationResponse } from '../../Response/Workspace/WorkspaceCreationResponse'
import { WorkspaceInvitationAcceptingRequestParams } from '../../Request/Workspace/WorkspaceInvitationAcceptingRequestParams'
import { WorkspaceInvitationAcceptingResponse } from '../../Response/Workspace/WorkspaceInvitationAcceptingResponse'
import { WorkspaceListRequestParams } from '../../Request/Workspace/WorkspaceListRequestParams'
import { WorkspaceListResponse } from '../../Response/Workspace/WorkspaceListResponse'
import { WorkspaceUserListRequestParams } from '../../Request/Workspace/WorkspaceUserListRequestParams'
import { WorkspaceUserListResponse } from '../../Response/Workspace/WorkspaceUserListResponse'
import { WorkspaceKeyshareInitiatingRequestParams } from '../../Request/Workspace/WorkspaceKeyshareInitiatingRequestParams'
import { WorkspaceKeyshareInitiatingResponse } from '../../Response/Workspace/WorkspaceKeyshareInitiatingResponse'
import { Paths } from './Paths'
import { WorkspaceServerInterface } from './WorkspaceServerInterface'
@@ -8,6 +18,41 @@ import { WorkspaceServerInterface } from './WorkspaceServerInterface'
export class WorkspaceServer implements WorkspaceServerInterface {
constructor(private httpService: HttpServiceInterface) {}
async initiateKeyshare(
params: WorkspaceKeyshareInitiatingRequestParams,
): Promise<WorkspaceKeyshareInitiatingResponse> {
const response = await this.httpService.post(
Paths.v1.initiateKeyshare(params.workspaceUuid, params.userUuid),
params,
)
return response as WorkspaceKeyshareInitiatingResponse
}
async listWorkspaceUsers(params: WorkspaceUserListRequestParams): Promise<WorkspaceUserListResponse> {
const response = await this.httpService.get(Paths.v1.listWorkspaceUsers(params.workspaceUuid), params)
return response as WorkspaceUserListResponse
}
async listWorkspaces(params: WorkspaceListRequestParams): Promise<WorkspaceListResponse> {
const response = await this.httpService.get(Paths.v1.listWorkspaces, params)
return response as WorkspaceListResponse
}
async acceptInvite(params: WorkspaceInvitationAcceptingRequestParams): Promise<WorkspaceInvitationAcceptingResponse> {
const response = await this.httpService.post(Paths.v1.acceptInvite(params.inviteUuid), params)
return response as WorkspaceInvitationAcceptingResponse
}
async inviteToWorkspace(params: WorkspaceInvitationRequestParams): Promise<WorkspaceInvitationResponse> {
const response = await this.httpService.post(Paths.v1.inviteToWorkspace(params.workspaceUuid), params)
return response as WorkspaceInvitationResponse
}
async createWorkspace(params: WorkspaceCreationRequestParams): Promise<WorkspaceCreationResponse> {
const response = await this.httpService.post(Paths.v1.createWorkspace, params)
@@ -1,6 +1,21 @@
import { WorkspaceInvitationRequestParams } from '../../Request/Workspace/WorkspaceInvitationRequestParams'
import { WorkspaceCreationRequestParams } from '../../Request/Workspace/WorkspaceCreationRequestParams'
import { WorkspaceInvitationResponse } from '../../Response/Workspace/WorkspaceInvitationResponse'
import { WorkspaceCreationResponse } from '../../Response/Workspace/WorkspaceCreationResponse'
import { WorkspaceInvitationAcceptingRequestParams } from '../../Request/Workspace/WorkspaceInvitationAcceptingRequestParams'
import { WorkspaceInvitationAcceptingResponse } from '../../Response/Workspace/WorkspaceInvitationAcceptingResponse'
import { WorkspaceListRequestParams } from '../../Request/Workspace/WorkspaceListRequestParams'
import { WorkspaceListResponse } from '../../Response/Workspace/WorkspaceListResponse'
import { WorkspaceUserListRequestParams } from '../../Request/Workspace/WorkspaceUserListRequestParams'
import { WorkspaceUserListResponse } from '../../Response/Workspace/WorkspaceUserListResponse'
import { WorkspaceKeyshareInitiatingRequestParams } from '../../Request/Workspace/WorkspaceKeyshareInitiatingRequestParams'
import { WorkspaceKeyshareInitiatingResponse } from '../../Response/Workspace/WorkspaceKeyshareInitiatingResponse'
export interface WorkspaceServerInterface {
createWorkspace(params: WorkspaceCreationRequestParams): Promise<WorkspaceCreationResponse>
listWorkspaces(params: WorkspaceListRequestParams): Promise<WorkspaceListResponse>
listWorkspaceUsers(params: WorkspaceUserListRequestParams): Promise<WorkspaceUserListResponse>
inviteToWorkspace(params: WorkspaceInvitationRequestParams): Promise<WorkspaceInvitationResponse>
acceptInvite(params: WorkspaceInvitationAcceptingRequestParams): Promise<WorkspaceInvitationAcceptingResponse>
initiateKeyshare(params: WorkspaceKeyshareInitiatingRequestParams): Promise<WorkspaceKeyshareInitiatingResponse>
}
+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.
## [2.7.19](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-11)
**Note:** Version bump only for package @standardnotes/components-meta
## [2.7.18](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-10)
**Note:** Version bump only for package @standardnotes/components-meta
## [2.7.17](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-10)
**Note:** Version bump only for package @standardnotes/components-meta
## [2.7.16](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-05)
**Note:** Version bump only for package @standardnotes/components-meta
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/components-meta",
"version": "2.7.16",
"version": "2.7.19",
"private": true,
"author": "Standard Notes.",
"main": "dist",
+66
View File
@@ -3,6 +3,72 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [3.23.212](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-12)
**Note:** Version bump only for package @standardnotes/desktop
## [3.23.211](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-12)
**Note:** Version bump only for package @standardnotes/desktop
## [3.23.210](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-12)
**Note:** Version bump only for package @standardnotes/desktop
## [3.23.209](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-12)
**Note:** Version bump only for package @standardnotes/desktop
## [3.23.208](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-11)
**Note:** Version bump only for package @standardnotes/desktop
## [3.23.207](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-11)
**Note:** Version bump only for package @standardnotes/desktop
## [3.23.206](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-11)
**Note:** Version bump only for package @standardnotes/desktop
## [3.23.205](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-11)
**Note:** Version bump only for package @standardnotes/desktop
## [3.23.204](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-11)
**Note:** Version bump only for package @standardnotes/desktop
## [3.23.203](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-11)
**Note:** Version bump only for package @standardnotes/desktop
## [3.23.202](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-10)
**Note:** Version bump only for package @standardnotes/desktop
## [3.23.201](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-10)
**Note:** Version bump only for package @standardnotes/desktop
## [3.23.200](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-09)
**Note:** Version bump only for package @standardnotes/desktop
## [3.23.199](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-08)
**Note:** Version bump only for package @standardnotes/desktop
## [3.23.198](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-07)
### Bug Fixes
* **desktop:** use different method to send messages across main and render without node integration in preload ([#1769](https://github.com/standardnotes/app/issues/1769)) ([20226c3](https://github.com/standardnotes/app/commit/20226c326945ad9dc524f6dcbfc1c1274befb652))
## [3.23.197](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-07)
**Note:** Version bump only for package @standardnotes/desktop
## [3.23.196](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-07)
**Note:** Version bump only for package @standardnotes/desktop
@@ -5,6 +5,7 @@ import { autoUpdater } from 'electron-updater'
import { action, autorun, computed, makeObservable, observable } from 'mobx'
import { MessageType } from '../../../test/TestIpcMessage'
import { AppState } from '../../AppState'
import { MessageToWebApp } from '../Shared/IpcMessages'
import { BackupsManagerInterface } from './Backups/BackupsManagerInterface'
import { StoreKeys } from './Store/StoreKeys'
import { updates as str } from './Strings'
@@ -113,12 +114,12 @@ export function setupUpdates(window: BrowserWindow, appState: AppState, backupsM
setInterval(checkUpdateSafety, oneHour)
autoUpdater.on('update-downloaded', (info: { version?: string }) => {
window.webContents.send('update-available', null)
window.webContents.send(MessageToWebApp.UpdateAvailable, null)
updateState.autoUpdateHasBeenDownloaded(info.version || null)
})
autoUpdater.on('error', logError)
autoUpdater.on('update-available', (info: { version?: string }) => {
autoUpdater.on(MessageToWebApp.UpdateAvailable, (info: { version?: string }) => {
updateState.checkedForUpdate(info.version || null)
if (updateState.enableAutoUpdate) {
const canUpdate = checkUpdateSafety()
@@ -166,6 +166,7 @@ async function createWindow(store: Store): Promise<Electron.BrowserWindow> {
spellcheck: true,
nodeIntegration: isTesting(),
contextIsolation: true,
sandbox: true,
preload: Paths.preloadJs,
},
})
@@ -1,42 +1,28 @@
import { IpcRendererEvent } from 'electron/renderer'
import { MessageToWebApp } from '../Shared/IpcMessages'
const { ipcRenderer } = require('electron')
const path = require('path')
const rendererPath = path.join('file://', __dirname, '/renderer.js')
const RemoteBridge = require('@electron/remote').getGlobal('RemoteBridge')
const { contextBridge } = require('electron')
type MainEventCallback = (event: IpcRendererEvent, value: any) => void
process.once('loaded', function () {
contextBridge.exposeInMainWorld('electronRemoteBridge', RemoteBridge.exposableValue)
listenForIpcEventsFromMainProcess()
contextBridge.exposeInMainWorld('electronMainEvents', {
handleUpdateAvailable: (callback: MainEventCallback) => ipcRenderer.on(MessageToWebApp.UpdateAvailable, callback),
handlePerformAutomatedBackup: (callback: MainEventCallback) =>
ipcRenderer.on(MessageToWebApp.PerformAutomatedBackup, callback),
handleFinishedSavingBackup: (callback: MainEventCallback) =>
ipcRenderer.on(MessageToWebApp.FinishedSavingBackup, callback),
handleWindowBlurred: (callback: MainEventCallback) => ipcRenderer.on(MessageToWebApp.WindowBlurred, callback),
handleWindowFocused: (callback: MainEventCallback) => ipcRenderer.on(MessageToWebApp.WindowFocused, callback),
handleInstallComponentComplete: (callback: MainEventCallback) =>
ipcRenderer.on(MessageToWebApp.InstallComponentComplete, callback),
})
})
function listenForIpcEventsFromMainProcess() {
const sendMessageToRenderProcess = (message: string, payload = {}) => {
window.postMessage(JSON.stringify({ message, data: payload }), rendererPath)
}
ipcRenderer.on(MessageToWebApp.UpdateAvailable, function (_event, data) {
sendMessageToRenderProcess(MessageToWebApp.UpdateAvailable, data)
})
ipcRenderer.on(MessageToWebApp.PerformAutomatedBackup, function (_event, data) {
sendMessageToRenderProcess(MessageToWebApp.PerformAutomatedBackup, data)
})
ipcRenderer.on(MessageToWebApp.FinishedSavingBackup, function (_event, data) {
sendMessageToRenderProcess(MessageToWebApp.FinishedSavingBackup, data)
})
ipcRenderer.on(MessageToWebApp.WindowBlurred, function (_event, data) {
sendMessageToRenderProcess(MessageToWebApp.WindowBlurred, data)
})
ipcRenderer.on(MessageToWebApp.WindowFocused, function (_event, data) {
sendMessageToRenderProcess(MessageToWebApp.WindowFocused, data)
})
ipcRenderer.on(MessageToWebApp.InstallComponentComplete, function (_event, data) {
sendMessageToRenderProcess(MessageToWebApp.InstallComponentComplete, data)
})
}
@@ -1,6 +1,6 @@
import { DesktopClientRequiresWebMethods } from '@web/Application/Device/DesktopSnjsExports'
import { StartApplication } from '@web/Application/Device/StartApplication'
import { MessageToWebApp } from '../Shared/IpcMessages'
import { IpcRendererEvent } from 'electron/renderer'
import { CrossProcessBridge } from './CrossProcessBridge'
import { DesktopDevice } from './DesktopDevice'
@@ -23,6 +23,7 @@ declare global {
purchaseUrl: string
startApplication: StartApplication
zip: any
electronMainEvents: any
}
}
@@ -41,8 +42,6 @@ const loadAndStartApplication = async () => {
window.device = await createDesktopDevice(remoteBridge)
window.startApplication(DEFAULT_SYNC_SERVER, window.device, window.enableUnfinishedFeatures, WEBSOCKET_URL)
listenForMessagesSentFromMainToPreloadToUs(window.device)
}
window.onload = () => {
@@ -134,35 +133,26 @@ async function configureWindow(remoteBridge: CrossProcessBridge) {
}
}
function listenForMessagesSentFromMainToPreloadToUs(device: DesktopDevice) {
window.addEventListener('message', async (event) => {
// We don't have access to the full file path.
if (event.origin !== 'file://') {
return
}
let payload
try {
payload = JSON.parse(event.data)
} catch (e) {
// message doesn't belong to us
return
}
const receiver = window.webClient
const message = payload.message
const data = payload.data
window.electronMainEvents.handleUpdateAvailable(() => {
window.webClient.updateAvailable()
})
if (message === MessageToWebApp.WindowBlurred) {
receiver.windowLostFocus()
} else if (message === MessageToWebApp.WindowFocused) {
receiver.windowGainedFocus()
} else if (message === MessageToWebApp.InstallComponentComplete) {
receiver.onComponentInstallationComplete(data.component, undefined)
} else if (message === MessageToWebApp.UpdateAvailable) {
receiver.updateAvailable()
} else if (message === MessageToWebApp.PerformAutomatedBackup) {
void device.downloadBackup()
} else if (message === MessageToWebApp.FinishedSavingBackup) {
receiver.didFinishBackup(data.success)
}
})
}
window.electronMainEvents.handlePerformAutomatedBackup(() => {
void window.device.downloadBackup()
})
window.electronMainEvents.handleFinishedSavingBackup((_: IpcRendererEvent, data: any) => {
window.webClient.didFinishBackup(data.success)
})
window.electronMainEvents.handleWindowBlurred(() => {
window.webClient.windowLostFocus()
})
window.electronMainEvents.handleWindowFocused(() => {
window.webClient.windowGainedFocus()
})
window.electronMainEvents.handleInstallComponentComplete((_: IpcRendererEvent, data: any) => {
window.webClient.onComponentInstallationComplete(data.component, undefined)
})
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "@standardnotes/desktop",
"main": "./app/dist/index.js",
"version": "3.23.196",
"version": "3.23.212",
"license": "AGPL-3.0-or-later",
"author": "Standard Notes.",
"private": true,
+30
View File
@@ -3,6 +3,36 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [1.18.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-12)
### Features
* replace private workspaces with private usernames ([#1783](https://github.com/standardnotes/app/issues/1783)) ([18c821d](https://github.com/standardnotes/app/commit/18c821d8eb51beb6f54211eb1d6eb454303044f5))
## [1.17.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-11)
**Note:** Version bump only for package @standardnotes/encryption
# [1.17.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-11)
### Features
* **api:** add listing workspace users ([fb41f65](https://github.com/standardnotes/app/commit/fb41f651eca45dd62314609c2f389d5dc683c36e))
## [1.16.3](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-11)
**Note:** Version bump only for package @standardnotes/encryption
## [1.16.2](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-10)
**Note:** Version bump only for package @standardnotes/encryption
## [1.16.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-10)
### Bug Fixes
* **api:** workspace creation arguments ([a275a45](https://github.com/standardnotes/app/commit/a275a45753abc4a29a89de5fe784260165297bbe))
# [1.16.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-06)
### Features
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/encryption",
"version": "1.16.0",
"version": "1.18.0",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
@@ -35,7 +35,7 @@
"typescript": "*"
},
"dependencies": {
"@standardnotes/common": "^1.32.0",
"@standardnotes/common": "^1.39.0",
"@standardnotes/models": "workspace:*",
"@standardnotes/responses": "workspace:*",
"@standardnotes/sncrypto-common": "workspace:*",
@@ -0,0 +1,19 @@
import { PureCryptoInterface } from '@standardnotes/sncrypto-common'
const PrivateUserNameV1 = 'StandardNotes-PrivateUsername-V1'
export async function ComputePrivateUsername(
crypto: PureCryptoInterface,
usernameInput: string,
): Promise<string | undefined> {
const result = await crypto.hmac256(
await crypto.sha256(PrivateUserNameV1),
await crypto.sha256(usernameInput.trim().toLowerCase()),
)
if (result == undefined) {
return undefined
}
return result
}
@@ -1,18 +0,0 @@
import { PureCryptoInterface } from '@standardnotes/sncrypto-common'
export async function ComputePrivateWorkspaceIdentifier(
crypto: PureCryptoInterface,
userphrase: string,
name: string,
): Promise<string | undefined> {
const identifier = await crypto.hmac256(
await crypto.sha256(name.trim().toLowerCase()),
await crypto.sha256(userphrase.trim().toLowerCase()),
)
if (identifier == undefined) {
return undefined
}
return identifier
}
+1 -1
View File
@@ -34,4 +34,4 @@ export * from './Types/EncryptedParameters'
export * from './Types/ItemAuthenticatedData'
export * from './Types/LegacyAttachedData'
export * from './Types/RootKeyEncryptedAuthenticatedData'
export * from './Workspace/PrivateWorkspace'
export * from './Username/PrivateUsername'
+18
View File
@@ -3,6 +3,24 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [1.53.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-11)
### Features
* **api:** add listing workspace users ([fb41f65](https://github.com/standardnotes/app/commit/fb41f651eca45dd62314609c2f389d5dc683c36e))
## [1.52.4](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-10)
### Bug Fixes
* dark mode not working in editors ([#1773](https://github.com/standardnotes/app/issues/1773)) ([27d2c95](https://github.com/standardnotes/app/commit/27d2c95b5b82645bf6c0372979f05236c83a10f6))
## [1.52.3](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-10)
### Bug Fixes
* **api:** workspace creation arguments ([a275a45](https://github.com/standardnotes/app/commit/a275a45753abc4a29a89de5fe784260165297bbe))
## [1.52.2](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-05)
**Note:** Version bump only for package @standardnotes/features
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/features",
"version": "1.52.2",
"version": "1.53.0",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
@@ -26,7 +26,7 @@
},
"dependencies": {
"@standardnotes/auth": "^3.19.4",
"@standardnotes/common": "^1.32.0",
"@standardnotes/common": "^1.39.0",
"@standardnotes/security": "^1.2.0",
"reflect-metadata": "^0.1.13"
},
@@ -21,7 +21,7 @@ export enum FeatureIdentifier {
AutobiographyTheme = 'org.standardnotes.theme-autobiography',
DynamicTheme = 'org.standardnotes.theme-dynamic',
FocusedTheme = 'org.standardnotes.theme-focus',
DarkTheme = 'org.standardnotes.theme-focus',
FocusMode = 'org.standardnotes.focus-mode',
FuturaTheme = 'org.standardnotes.theme-futura',
MidnightTheme = 'org.standardnotes.theme-midnight',
+4 -4
View File
@@ -68,10 +68,10 @@ export function themes(): ThemeFeatureDescription[] {
},
})
const focus: ThemeFeatureDescription = FillThemeComponentDefaults({
const dark: ThemeFeatureDescription = FillThemeComponentDefaults({
availableInSubscriptions: [SubscriptionName.PlusPlan, SubscriptionName.ProPlan],
name: 'Focus',
identifier: FeatureIdentifier.FocusedTheme,
name: 'Dark',
identifier: FeatureIdentifier.DarkTheme,
permission_name: PermissionName.FocusedTheme,
description: 'For when you need to go in.',
thumbnail_url: 'https://s3.amazonaws.com/standard-notes/screenshots/models/themes/focus-with-mobile.jpg',
@@ -109,5 +109,5 @@ export function themes(): ThemeFeatureDescription[] {
description: 'A smart theme that minimizes the tags and notes panels when they are not in use.',
})
return [midnight, futura, solarizedDark, autobiography, focus, titanium, dynamic]
return [midnight, futura, solarizedDark, autobiography, dark, titanium, dynamic]
}
+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.24.2](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-12)
**Note:** Version bump only for package @standardnotes/filepicker
## [1.24.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-11)
**Note:** Version bump only for package @standardnotes/filepicker
# [1.24.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-11)
### Features
* **api:** add listing workspace users ([fb41f65](https://github.com/standardnotes/app/commit/fb41f651eca45dd62314609c2f389d5dc683c36e))
## [1.23.15](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-11)
**Note:** Version bump only for package @standardnotes/filepicker
## [1.23.14](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-10)
**Note:** Version bump only for package @standardnotes/filepicker
## [1.23.13](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-10)
### Bug Fixes
* **api:** workspace creation arguments ([a275a45](https://github.com/standardnotes/app/commit/a275a45753abc4a29a89de5fe784260165297bbe))
## [1.23.12](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-06)
**Note:** Version bump only for package @standardnotes/filepicker
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/filepicker",
"version": "1.23.12",
"version": "1.24.2",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
@@ -31,7 +31,7 @@
"ts-node": "^10.5.0"
},
"dependencies": {
"@standardnotes/common": "^1.32.0",
"@standardnotes/common": "^1.39.0",
"@standardnotes/files": "workspace:*",
"@standardnotes/utils": "workspace:*",
"@types/wicg-file-system-access": "^2020.9.5",
+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.11.2](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-12)
**Note:** Version bump only for package @standardnotes/files
## [1.11.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-11)
**Note:** Version bump only for package @standardnotes/files
# [1.11.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-11)
### Features
* **api:** add listing workspace users ([fb41f65](https://github.com/standardnotes/app/commit/fb41f651eca45dd62314609c2f389d5dc683c36e))
## [1.10.15](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-11)
**Note:** Version bump only for package @standardnotes/files
## [1.10.14](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-10)
**Note:** Version bump only for package @standardnotes/files
## [1.10.13](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-10)
### Bug Fixes
* **api:** workspace creation arguments ([a275a45](https://github.com/standardnotes/app/commit/a275a45753abc4a29a89de5fe784260165297bbe))
## [1.10.12](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-06)
**Note:** Version bump only for package @standardnotes/files
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/files",
"version": "1.10.12",
"version": "1.11.2",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
@@ -30,7 +30,7 @@
"ts-jest": "^28.0.5"
},
"dependencies": {
"@standardnotes/common": "^1.32.0",
"@standardnotes/common": "^1.39.0",
"@standardnotes/encryption": "workspace:*",
"@standardnotes/models": "workspace:*",
"@standardnotes/responses": "workspace:*",
+82
View File
@@ -3,6 +3,88 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [3.42.5](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-12)
**Note:** Version bump only for package @standardnotes/mobile
## [3.42.4](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-12)
**Note:** Version bump only for package @standardnotes/mobile
## [3.42.3](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-12)
**Note:** Version bump only for package @standardnotes/mobile
## [3.42.2](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-12)
**Note:** Version bump only for package @standardnotes/mobile
## [3.42.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-11)
**Note:** Version bump only for package @standardnotes/mobile
# [3.42.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-11)
### Features
* item linking ([#1779](https://github.com/standardnotes/app/issues/1779)) ([e3f2842](https://github.com/standardnotes/app/commit/e3f28421ff042c635ad2ae645c102c27e3e3f9c7))
## [3.41.10](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-11)
### Bug Fixes
* **mobile:** issue with black screen on iOS ([#1777](https://github.com/standardnotes/app/issues/1777)) ([9fc77d8](https://github.com/standardnotes/app/commit/9fc77d861e501d63ee97875bba2ddda51d14fd0b))
## [3.41.9](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-11)
**Note:** Version bump only for package @standardnotes/mobile
## [3.41.8](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-11)
**Note:** Version bump only for package @standardnotes/mobile
## [3.41.7](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-11)
**Note:** Version bump only for package @standardnotes/mobile
## [3.41.6](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-11)
**Note:** Version bump only for package @standardnotes/mobile
## [3.41.5](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-10)
**Note:** Version bump only for package @standardnotes/mobile
## [3.41.4](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-10)
**Note:** Version bump only for package @standardnotes/mobile
## [3.41.3](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-09)
**Note:** Version bump only for package @standardnotes/mobile
## [3.41.2](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-08)
**Note:** Version bump only for package @standardnotes/mobile
## [3.41.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-07)
**Note:** Version bump only for package @standardnotes/mobile
# [3.41.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-07)
### Bug Fixes
* **mobile:** disable landscape for iPhone ([a365c17](https://github.com/standardnotes/app/commit/a365c17c46f38e6d8cacade47542fd4ec625bc0d))
### Features
* **mobile:** delete account option in settings ([#1768](https://github.com/standardnotes/app/issues/1768)) ([7d21046](https://github.com/standardnotes/app/commit/7d21046b5189eb14a5957bda6fa788d743d8ebb6))
## [3.40.2](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-07)
**Note:** Version bump only for package @standardnotes/mobile
## [3.40.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-07)
**Note:** Version bump only for package @standardnotes/mobile
+2 -2
View File
@@ -302,7 +302,7 @@ PODS:
- React
- react-native-version-info (1.1.1):
- React-Core
- react-native-webview (11.17.2):
- react-native-webview (11.23.1):
- React-Core
- React-perflogger (0.67.4)
- React-RCTActionSheet (0.67.4):
@@ -719,7 +719,7 @@ SPEC CHECKSUMS:
react-native-sodium-jsi: c8901320767d00385f9111bc95ba25aaa9a29890
react-native-static-server: 880d9b697ef68722e6fa06ae06e6b843e906ed0b
react-native-version-info: a106f23009ac0db4ee00de39574eb546682579b9
react-native-webview: 380c1a03ec94b7ed764dac8db1e7c9952d08c93a
react-native-webview: d33e2db8925d090871ffeb232dfa50cb3a727581
React-perflogger: 0afaf2f01a47fd0fc368a93bfbb5bd3b26db6e7f
React-RCTActionSheet: 59f35c4029e0b532fc42114241a06e170b7431a2
React-RCTAnimation: aae4f4bed122e78bdab72f7118d291d70a932ce2
@@ -138,6 +138,10 @@
<string>UIInterfaceOrientationLandscapeRight</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
</array>
<key>UISupportedInterfaceOrientations~iphone</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
</array>
<key>UIViewControllerBasedStatusBarAppearance</key>
<false/>
<key>supportsAlternateIcons</key>
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/mobile",
"version": "3.40.1",
"version": "3.42.5",
"author": "Standard Notes.",
"private": true,
"license": "AGPL-3.0-or-later",
@@ -80,7 +80,7 @@
"react-native-url-polyfill": "^1.3.0",
"react-native-vector-icons": "^9.1.0",
"react-native-version-info": "^1.1.1",
"react-native-webview": "11.17.2",
"react-native-webview": "11.23.1",
"react-native-zip-archive": "^6.0.6",
"react-navigation-header-buttons": "^9.0.1",
"styled-components": "5.3.5"
+1 -1
View File
@@ -68,7 +68,7 @@ export const useFiles = ({ note }: Props) => {
const filesService = application.getFilesService()
const reloadAttachedFiles = useCallback(() => {
setAttachedFiles(application.items.getFilesForNote(note).sort(filesService.sortByName))
setAttachedFiles(application.items.getSortedFilesLinkingToItem(note).sort(filesService.sortByName))
}, [application.items, filesService.sortByName, note])
const reloadAllFiles = useCallback(() => {
@@ -204,6 +204,12 @@ const MobileWebAppContents = ({ destroyAndReload }: { destroyAndReload: () => vo
onError={(err) => console.error('An error has occurred', err)}
onHttpError={() => console.error('An HTTP error occurred')}
onMessage={onMessage}
onContentProcessDidTerminate={() => {
webViewRef.current?.reload()
}}
onRenderProcessGone={() => {
webViewRef.current?.reload()
}}
allowFileAccess={true}
allowUniversalAccessFromFileURLs={true}
injectedJavaScriptBeforeContentLoaded={injectedJS}
@@ -0,0 +1,50 @@
import { ApplicationContext } from '@Root/ApplicationContext'
import { ButtonCell } from '@Root/Components/ButtonCell'
import { SectionHeader } from '@Root/Components/SectionHeader'
import { TableSection } from '@Root/Components/TableSection'
import { ButtonType } from '@standardnotes/snjs'
import React, { useContext, useState } from 'react'
import { RegularView } from './AuthSection.styled'
export const DeleteSection = () => {
const application = useContext(ApplicationContext)
const [deleting, setDeleting] = useState(false)
const deleteAccount = async () => {
const message =
"This action is irreversible. After deletion completes, you will be signed out on all devices, and this application will exit. If you have an active paid subscription, cancel the subscription first. Otherwise, if you'd like to keep the subscription, you can re-register with the same email after deletion, and your subscription will be linked back up with your account."
const confirmed = await application!.alertService.confirm(
message,
'Are you sure?',
'Delete Account',
ButtonType.Danger,
)
if (!confirmed) {
return
}
setDeleting(true)
const result = await application!.user.deleteAccount()
if (result.error) {
void application!.alertService.alert('An error occurred while deleting your account. Please try again.')
}
}
return (
<RegularView>
<SectionHeader />
<TableSection>
<ButtonCell
first
last
important
leftAligned={true}
title={deleting ? 'Deleting...' : 'Delete Account'}
onPress={deleteAccount}
></ButtonCell>
</TableSection>
</RegularView>
)
}
@@ -153,7 +153,7 @@ export const WorkspacesSection = () => {
}
await appGroup.unloadCurrentAndCreateNewDescriptor()
}, [WorkspaceAction.AddAnother, appGroup, applicationDescriptors, getWorkspaceActionConfirmation])
}, [WorkspaceAction.AddAnother, appGroup, getWorkspaceActionConfirmation])
const signOutAllWorkspaces = useCallback(async () => {
try {
@@ -8,6 +8,7 @@ import { ApplicationEvent, FeatureIdentifier, FeatureStatus } from '@standardnot
import React, { useCallback, useEffect, useState } from 'react'
import { AuthSection } from './Sections/AuthSection'
import { CompanySection } from './Sections/CompanySection'
import { DeleteSection } from './Sections/DeleteSection'
import { EncryptionSection } from './Sections/EncryptionSection'
import { NewMobileSection } from './Sections/NewMobilePreview'
import { OptionsSection } from './Sections/OptionsSection'
@@ -68,6 +69,7 @@ export const Settings = (props: Props) => {
<ProtectionsSection title="Protections" protectionsAvailable={protectionsAvailable} />
<EncryptionSection encryptionAvailable={!!encryptionAvailable} title={'Encryption Status'} />
<CompanySection title="Standard Notes" />
{application.hasAccount() && <DeleteSection />}
</Container>
)
}
@@ -139,7 +139,7 @@ export const NoteSideMenu = React.memo((props: Props) => {
setAttachedFilesLength(0)
return
}
setAttachedFilesLength(application.items.getFilesForNote(note).length)
setAttachedFilesLength(application.items.getSortedFilesLinkingToItem(note).length)
}, [application, note])
useEffect(() => {
@@ -147,7 +147,7 @@ export const NoteSideMenu = React.memo((props: Props) => {
return
}
const removeFilesObserver = application.streamItems(ContentType.File, () => {
setAttachedFilesLength(application.items.getFilesForNote(note).length)
setAttachedFilesLength(application.items.getSortedFilesLinkingToItem(note).length)
})
return () => {
removeFilesObserver()
+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.27.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-11)
### Features
* item linking ([#1779](https://github.com/standardnotes/app/issues/1779)) ([e3f2842](https://github.com/standardnotes/app/commit/e3f28421ff042c635ad2ae645c102c27e3e3f9c7))
# [1.26.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-11)
### Features
* **api:** add listing workspace users ([fb41f65](https://github.com/standardnotes/app/commit/fb41f651eca45dd62314609c2f389d5dc683c36e))
# [1.25.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-11)
### Features
* **api:** add listing workspaces ([8376381](https://github.com/standardnotes/app/commit/837638198112df24c30e53803a7ddf5f841975b0))
## [1.24.2](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-10)
**Note:** Version bump only for package @standardnotes/models
## [1.24.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-10)
### Bug Fixes
* **api:** workspace creation arguments ([a275a45](https://github.com/standardnotes/app/commit/a275a45753abc4a29a89de5fe784260165297bbe))
# [1.24.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-06)
### Features
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/models",
"version": "1.24.0",
"version": "1.27.0",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
@@ -31,7 +31,7 @@
"typescript": "*"
},
"dependencies": {
"@standardnotes/common": "^1.32.0",
"@standardnotes/common": "^1.39.0",
"@standardnotes/features": "workspace:*",
"@standardnotes/responses": "workspace:*",
"@standardnotes/utils": "workspace:*",
@@ -1,8 +1,8 @@
import { ContentType } from '@standardnotes/common'
import { ContenteReferenceType } from './ContenteReferenceType'
import { ContentReferenceType } from './ContenteReferenceType'
export interface AnonymousReference {
uuid: string
content_type: ContentType
reference_type: ContenteReferenceType
reference_type: ContentReferenceType
}
@@ -1,5 +1,7 @@
export enum ContenteReferenceType {
export enum ContentReferenceType {
TagToParentTag = 'TagToParentTag',
FileToNote = 'FileToNote',
TagToFile = 'TagToFile',
FileToNote = 'FileToNote',
FileToFile = 'FileToFile',
NoteToNote = 'NoteToNote',
}
@@ -0,0 +1,8 @@
import { ContentType } from '@standardnotes/common'
import { AnonymousReference } from './AnonymousReference'
import { ContentReferenceType } from './ContenteReferenceType'
export interface FileToFileReference extends AnonymousReference {
content_type: ContentType.File
reference_type: ContentReferenceType.FileToFile
}
@@ -1,8 +1,8 @@
import { ContentType } from '@standardnotes/common'
import { AnonymousReference } from './AnonymousReference'
import { ContenteReferenceType } from './ContenteReferenceType'
import { ContentReferenceType } from './ContenteReferenceType'
export interface FileToNoteReference extends AnonymousReference {
content_type: ContentType.Note
reference_type: ContenteReferenceType.FileToNote
reference_type: ContentReferenceType.FileToNote
}
@@ -1,7 +1,7 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import { ContentType } from '@standardnotes/common'
import { ItemInterface } from '../Item/Interfaces/ItemInterface'
import { ContenteReferenceType } from './ContenteReferenceType'
import { ContentReferenceType } from './ContenteReferenceType'
import { ContentReference } from './ContentReference'
import { LegacyAnonymousReference } from './LegacyAnonymousReference'
import { LegacyTagToNoteReference } from './LegacyTagToNoteReference'
@@ -26,5 +26,5 @@ export const isLegacyTagToNoteReference = (
}
export const isTagToParentTagReference = (x: ContentReference): x is TagToParentTagReference => {
return isReference(x) && x.reference_type === ContenteReferenceType.TagToParentTag
return isReference(x) && x.reference_type === ContentReferenceType.TagToParentTag
}
@@ -0,0 +1,8 @@
import { ContentType } from '@standardnotes/common'
import { AnonymousReference } from './AnonymousReference'
import { ContentReferenceType } from './ContenteReferenceType'
export interface NoteToNoteReference extends AnonymousReference {
content_type: ContentType.Note
reference_type: ContentReferenceType.NoteToNote
}
@@ -1,8 +1,8 @@
import { ContentType } from '@standardnotes/common'
import { AnonymousReference } from './AnonymousReference'
import { ContenteReferenceType } from './ContenteReferenceType'
import { ContentReferenceType } from './ContenteReferenceType'
export interface TagToFileReference extends AnonymousReference {
content_type: ContentType.File
reference_type: ContenteReferenceType.TagToFile
reference_type: ContentReferenceType.TagToFile
}
@@ -1,8 +1,8 @@
import { ContentType } from '@standardnotes/common'
import { AnonymousReference } from './AnonymousReference'
import { ContenteReferenceType } from './ContenteReferenceType'
import { ContentReferenceType } from './ContenteReferenceType'
export interface TagToParentTagReference extends AnonymousReference {
content_type: ContentType.Tag
reference_type: ContenteReferenceType.TagToParentTag
reference_type: ContentReferenceType.TagToParentTag
}
@@ -0,0 +1,10 @@
import { WorkspaceType } from '@standardnotes/common'
export type Workspace = {
uuid: string
type: WorkspaceType
name: string | null
keyRotationIndex: number
createdAt: number
updatedAt: number
}
@@ -0,0 +1,16 @@
import { Uuid, WorkspaceAccessLevel, WorkspaceUserStatus } from '@standardnotes/common'
export type WorkspaceUser = {
uuid: Uuid
accessLevel: WorkspaceAccessLevel
userUuid: Uuid
userDisplayName: string | null
workspaceUuid: Uuid
encryptedWorkspaceKey: string | null
publicKey: string | null
encryptedPrivateKey: string | null
status: WorkspaceUserStatus
keyRotationIndex: number
createdAt: number
updatedAt: number
}
@@ -1,10 +1,10 @@
import { NoteContent } from './../../../Syncable/Note/NoteContent'
import { NoteContent } from '../../../Syncable/Note/NoteContent'
import { ContentType } from '@standardnotes/common'
import { DecryptedItem, EncryptedItem } from '../../../Abstract/Item'
import { DecryptedPayload, EncryptedPayload, PayloadTimestampDefaults } from '../../../Abstract/Payload'
import { ItemCollection } from './ItemCollection'
import { FillItemContent } from '../../../Abstract/Content/ItemContent'
import { TagNotesIndex } from './TagNotesIndex'
import { TagItemsIndex } from './TagItemsIndex'
import { ItemDelta } from '../../Index/ItemDelta'
import { AnyItemInterface } from '../../../Abstract/Item/Interfaces/UnionTypes'
@@ -24,10 +24,10 @@ describe('tag notes index', () => {
return new EncryptedItem(payload)
}
const createDecryptedItem = (uuid?: string) => {
const createDecryptedItem = (uuid?: string, content_type = ContentType.Note) => {
const payload = new DecryptedPayload({
uuid: uuid || String(Math.random()),
content_type: ContentType.Note,
content_type,
content: FillItemContent<NoteContent>({
title: 'foo',
}),
@@ -46,20 +46,33 @@ describe('tag notes index', () => {
}
}
it('should count both notes and files', () => {
const collection = new ItemCollection()
const index = new TagItemsIndex(collection)
const decryptedNote = createDecryptedItem('note')
const decryptedFile = createDecryptedItem('file')
collection.set([decryptedNote, decryptedFile])
index.onChange(createChangeDelta(decryptedNote))
index.onChange(createChangeDelta(decryptedFile))
expect(index.allCountableItemsCount()).toEqual(2)
})
it('should decrement count after decrypted note becomes errored', () => {
const collection = new ItemCollection()
const index = new TagNotesIndex(collection)
const index = new TagItemsIndex(collection)
const decryptedItem = createDecryptedItem()
collection.set(decryptedItem)
index.onChange(createChangeDelta(decryptedItem))
expect(index.allCountableNotesCount()).toEqual(1)
expect(index.allCountableItemsCount()).toEqual(1)
const encryptedItem = createEncryptedItem(decryptedItem.uuid)
collection.set(encryptedItem)
index.onChange(createChangeDelta(encryptedItem))
expect(index.allCountableNotesCount()).toEqual(0)
expect(index.allCountableItemsCount()).toEqual(0)
})
})
@@ -7,22 +7,22 @@ import { ItemDelta } from '../../Index/ItemDelta'
import { isDecryptedItem, ItemInterface } from '../../../Abstract/Item'
type AllNotesUuidSignifier = undefined
export type TagNoteCountChangeObserver = (tagUuid: Uuid | AllNotesUuidSignifier) => void
export type TagItemCountChangeObserver = (tagUuid: Uuid | AllNotesUuidSignifier) => void
export class TagNotesIndex implements SNIndex {
private tagToNotesMap: Partial<Record<Uuid, Set<Uuid>>> = {}
private allCountableNotes = new Set<Uuid>()
export class TagItemsIndex implements SNIndex {
private tagToItemsMap: Partial<Record<Uuid, Set<Uuid>>> = {}
private allCountableItems = new Set<Uuid>()
constructor(private collection: ItemCollection, public observers: TagNoteCountChangeObserver[] = []) {}
constructor(private collection: ItemCollection, public observers: TagItemCountChangeObserver[] = []) {}
private isNoteCountable = (note: ItemInterface) => {
if (isDecryptedItem(note)) {
return !note.archived && !note.trashed
private isItemCountable = (item: ItemInterface) => {
if (isDecryptedItem(item)) {
return !item.archived && !item.trashed
}
return false
}
public addCountChangeObserver(observer: TagNoteCountChangeObserver): () => void {
public addCountChangeObserver(observer: TagItemCountChangeObserver): () => void {
this.observers.push(observer)
const thislessEventObservers = this.observers
@@ -37,30 +37,32 @@ export class TagNotesIndex implements SNIndex {
}
}
public allCountableNotesCount(): number {
return this.allCountableNotes.size
public allCountableItemsCount(): number {
return this.allCountableItems.size
}
public countableNotesForTag(tag: SNTag): number {
return this.tagToNotesMap[tag.uuid]?.size || 0
public countableItemsForTag(tag: SNTag): number {
return this.tagToItemsMap[tag.uuid]?.size || 0
}
public onChange(delta: ItemDelta): void {
const notes = [...delta.changed, ...delta.inserted, ...delta.discarded].filter(
(i) => i.content_type === ContentType.Note,
const items = [...delta.changed, ...delta.inserted, ...delta.discarded].filter(
(i) => i.content_type === ContentType.Note || i.content_type === ContentType.File,
)
const tags = [...delta.changed, ...delta.inserted].filter(isDecryptedItem).filter(isTag)
this.receiveNoteChanges(notes)
this.receiveItemChanges(items)
this.receiveTagChanges(tags)
}
private receiveTagChanges(tags: SNTag[]): void {
for (const tag of tags) {
const uuids = tag.noteReferences.map((ref) => ref.uuid)
const countableUuids = uuids.filter((uuid) => this.allCountableNotes.has(uuid))
const previousSet = this.tagToNotesMap[tag.uuid]
this.tagToNotesMap[tag.uuid] = new Set(countableUuids)
const uuids = tag.references
.filter((ref) => ref.content_type === ContentType.Note || ref.content_type === ContentType.File)
.map((ref) => ref.uuid)
const countableUuids = uuids.filter((uuid) => this.allCountableItems.has(uuid))
const previousSet = this.tagToItemsMap[tag.uuid]
this.tagToItemsMap[tag.uuid] = new Set(countableUuids)
if (previousSet?.size !== countableUuids.length) {
this.notifyObservers(tag.uuid)
@@ -68,26 +70,26 @@ export class TagNotesIndex implements SNIndex {
}
}
private receiveNoteChanges(notes: ItemInterface[]): void {
const previousAllCount = this.allCountableNotes.size
private receiveItemChanges(items: ItemInterface[]): void {
const previousAllCount = this.allCountableItems.size
for (const note of notes) {
const isCountable = this.isNoteCountable(note)
for (const item of items) {
const isCountable = this.isItemCountable(item)
if (isCountable) {
this.allCountableNotes.add(note.uuid)
this.allCountableItems.add(item.uuid)
} else {
this.allCountableNotes.delete(note.uuid)
this.allCountableItems.delete(item.uuid)
}
const associatedTagUuids = this.collection.uuidsThatReferenceUuid(note.uuid)
const associatedTagUuids = this.collection.uuidsThatReferenceUuid(item.uuid)
for (const tagUuid of associatedTagUuids) {
const set = this.setForTag(tagUuid)
const previousCount = set.size
if (isCountable) {
set.add(note.uuid)
set.add(item.uuid)
} else {
set.delete(note.uuid)
set.delete(item.uuid)
}
if (previousCount !== set.size) {
this.notifyObservers(tagUuid)
@@ -95,16 +97,16 @@ export class TagNotesIndex implements SNIndex {
}
}
if (previousAllCount !== this.allCountableNotes.size) {
if (previousAllCount !== this.allCountableItems.size) {
this.notifyObservers(undefined)
}
}
private setForTag(uuid: Uuid): Set<Uuid> {
let set = this.tagToNotesMap[uuid]
let set = this.tagToItemsMap[uuid]
if (!set) {
set = new Set()
this.tagToNotesMap[uuid] = set
this.tagToItemsMap[uuid] = set
}
return set
}
@@ -1,9 +1,10 @@
import { ContentType } from '@standardnotes/common'
import { SNNote } from '../Note/Note'
import { FileContent } from './File'
import { FileContent, FileItem } from './File'
import { FileToNoteReference } from '../../Abstract/Reference/FileToNoteReference'
import { ContenteReferenceType } from '../../Abstract/Reference/ContenteReferenceType'
import { ContentReferenceType } from '../../Abstract/Reference/ContenteReferenceType'
import { DecryptedItemMutator } from '../../Abstract/Item/Mutator/DecryptedItemMutator'
import { FileToFileReference } from '../../Abstract/Reference/FileToFileReference'
export class FileMutator extends DecryptedItemMutator<FileContent> {
set name(newName: string) {
@@ -16,7 +17,7 @@ export class FileMutator extends DecryptedItemMutator<FileContent> {
public addNote(note: SNNote): void {
const reference: FileToNoteReference = {
reference_type: ContenteReferenceType.FileToNote,
reference_type: ContentReferenceType.FileToNote,
content_type: ContentType.Note,
uuid: note.uuid,
}
@@ -30,4 +31,22 @@ export class FileMutator extends DecryptedItemMutator<FileContent> {
const references = this.immutableItem.references.filter((ref) => ref.uuid !== note.uuid)
this.mutableContent.references = references
}
public addFile(file: FileItem): void {
if (this.immutableItem.isReferencingItem(file)) {
return
}
const reference: FileToFileReference = {
uuid: file.uuid,
content_type: ContentType.File,
reference_type: ContentReferenceType.FileToFile,
}
this.mutableContent.references.push(reference)
}
public removeFile(file: FileItem): void {
this.mutableContent.references = this.mutableContent.references.filter((r) => r.uuid !== file.uuid)
}
}
@@ -1,6 +1,10 @@
import { AppDataField } from '../../Abstract/Item/Types/AppDataField'
import { NoteContent } from './NoteContent'
import { DecryptedItemMutator } from '../../Abstract/Item/Mutator/DecryptedItemMutator'
import { SNNote } from './Note'
import { NoteToNoteReference } from '../../Abstract/Reference/NoteToNoteReference'
import { ContentType } from '@standardnotes/common'
import { ContentReferenceType } from '../../Abstract/Item'
export class NoteMutator extends DecryptedItemMutator<NoteContent> {
set title(title: string) {
@@ -38,4 +42,22 @@ export class NoteMutator extends DecryptedItemMutator<NoteContent> {
this.mutableContent.spellcheck = !this.mutableContent.spellcheck
}
}
public addNote(note: SNNote): void {
if (this.immutableItem.isReferencingItem(note)) {
return
}
const reference: NoteToNoteReference = {
uuid: note.uuid,
content_type: ContentType.Note,
reference_type: ContentReferenceType.NoteToNote,
}
this.mutableContent.references.push(reference)
}
public removeNote(note: SNNote): void {
this.mutableContent.references = this.mutableContent.references.filter((r) => r.uuid !== note.uuid)
}
}
@@ -1,5 +1,5 @@
import { ContentType } from '@standardnotes/common'
import { ContenteReferenceType, MutationType } from '../../Abstract/Item'
import { ContentReferenceType, MutationType } from '../../Abstract/Item'
import { createFile, createTag } from '../../Utilities/Test/SpecUtils'
import { SNTag } from './Tag'
import { TagMutator } from './TagMutator'
@@ -16,7 +16,7 @@ describe('tag mutator', () => {
expect(result.content.references[0]).toEqual({
uuid: file.uuid,
content_type: ContentType.File,
reference_type: ContenteReferenceType.TagToFile,
reference_type: ContentReferenceType.TagToFile,
})
})
@@ -4,7 +4,7 @@ import { FileItem } from '../File'
import { SNNote } from '../Note'
import { isTagToParentTagReference } from '../../Abstract/Reference/Functions'
import { TagToParentTagReference } from '../../Abstract/Reference/TagToParentTagReference'
import { ContenteReferenceType } from '../../Abstract/Reference/ContenteReferenceType'
import { ContentReferenceType } from '../../Abstract/Reference/ContenteReferenceType'
import { DecryptedItemMutator } from '../../Abstract/Item/Mutator/DecryptedItemMutator'
import { TagToFileReference } from '../../Abstract/Reference/TagToFileReference'
@@ -21,7 +21,7 @@ export class TagMutator extends DecryptedItemMutator<TagContent> {
const references = this.immutableItem.references.filter((ref) => !isTagToParentTagReference(ref))
const reference: TagToParentTagReference = {
reference_type: ContenteReferenceType.TagToParentTag,
reference_type: ContentReferenceType.TagToParentTag,
content_type: ContentType.Tag,
uuid: tag.uuid,
}
@@ -41,7 +41,7 @@ export class TagMutator extends DecryptedItemMutator<TagContent> {
}
const reference: TagToFileReference = {
reference_type: ContenteReferenceType.TagToFile,
reference_type: ContentReferenceType.TagToFile,
content_type: ContentType.File,
uuid: file.uuid,
}
+3 -1
View File
@@ -28,6 +28,8 @@ export * from './Api/Subscription/Invitation'
export * from './Api/Subscription/InvitationStatus'
export * from './Api/Subscription/InviteeIdentifierType'
export * from './Api/Subscription/InviterIdentifierType'
export * from './Api/Workspace/Workspace'
export * from './Api/Workspace/WorkspaceUser'
export * from './Device/Environment'
export * from './Device/Platform'
export * from './Local/KeyParams/RootKeyParamsInterface'
@@ -36,7 +38,7 @@ export * from './Local/RootKey/RootKeyContent'
export * from './Local/RootKey/RootKeyInterface'
export * from './Runtime/Collection/CollectionSort'
export * from './Runtime/Collection/Item/ItemCollection'
export * from './Runtime/Collection/Item/TagNotesIndex'
export * from './Runtime/Collection/Item/TagItemsIndex'
export * from './Runtime/Collection/Payload/ImmutablePayloadCollection'
export * from './Runtime/Collection/Payload/PayloadCollection'
export * from './Runtime/Deltas'
+72
View File
@@ -3,6 +3,78 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.3.140](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-12)
**Note:** Version bump only for package @standardnotes/releases
## [1.3.139](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-12)
**Note:** Version bump only for package @standardnotes/releases
## [1.3.138](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-12)
**Note:** Version bump only for package @standardnotes/releases
## [1.3.137](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-12)
**Note:** Version bump only for package @standardnotes/releases
## [1.3.136](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-11)
**Note:** Version bump only for package @standardnotes/releases
## [1.3.135](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-11)
**Note:** Version bump only for package @standardnotes/releases
## [1.3.134](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-11)
**Note:** Version bump only for package @standardnotes/releases
## [1.3.133](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-11)
**Note:** Version bump only for package @standardnotes/releases
## [1.3.132](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-11)
**Note:** Version bump only for package @standardnotes/releases
## [1.3.131](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-11)
**Note:** Version bump only for package @standardnotes/releases
## [1.3.130](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-11)
**Note:** Version bump only for package @standardnotes/releases
## [1.3.129](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-10)
**Note:** Version bump only for package @standardnotes/releases
## [1.3.128](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-10)
**Note:** Version bump only for package @standardnotes/releases
## [1.3.127](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-09)
**Note:** Version bump only for package @standardnotes/releases
## [1.3.126](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-08)
**Note:** Version bump only for package @standardnotes/releases
## [1.3.125](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-07)
**Note:** Version bump only for package @standardnotes/releases
## [1.3.124](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-07)
**Note:** Version bump only for package @standardnotes/releases
## [1.3.123](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-07)
**Note:** Version bump only for package @standardnotes/releases
## [1.3.122](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-07)
**Note:** Version bump only for package @standardnotes/releases
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/releases",
"version": "1.3.122",
"version": "1.3.140",
"license": "AGPL-3.0-or-later",
"main": "dist/releases.json",
"types": "dist/index.d.ts",
+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.
# [1.11.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-11)
### Features
* **api:** add listing workspace users ([fb41f65](https://github.com/standardnotes/app/commit/fb41f651eca45dd62314609c2f389d5dc683c36e))
## [1.10.6](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-10)
**Note:** Version bump only for package @standardnotes/responses
## [1.10.5](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-10)
### Bug Fixes
* **api:** workspace creation arguments ([a275a45](https://github.com/standardnotes/app/commit/a275a45753abc4a29a89de5fe784260165297bbe))
## [1.10.4](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-05)
**Note:** Version bump only for package @standardnotes/responses
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/responses",
"version": "1.10.4",
"version": "1.11.0",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
@@ -31,7 +31,7 @@
"ts-jest": "^28.0.5"
},
"dependencies": {
"@standardnotes/common": "^1.32.0",
"@standardnotes/common": "^1.39.0",
"@standardnotes/features": "workspace:*",
"@standardnotes/security": "^1.1.0",
"reflect-metadata": "^0.1.13"
+56
View File
@@ -3,6 +3,62 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.32.2](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-12)
**Note:** Version bump only for package @standardnotes/services
## [1.32.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-12)
**Note:** Version bump only for package @standardnotes/services
# [1.32.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-12)
### Features
* **api:** add keyshare initiation for workspaces ([fabce1f](https://github.com/standardnotes/app/commit/fabce1f7aca5c9caaa5bb3908b2dc68063c690d0))
# [1.31.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-11)
### Features
* item linking ([#1779](https://github.com/standardnotes/app/issues/1779)) ([e3f2842](https://github.com/standardnotes/app/commit/e3f28421ff042c635ad2ae645c102c27e3e3f9c7))
# [1.30.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-11)
### Features
* **api:** add listing workspace users ([fb41f65](https://github.com/standardnotes/app/commit/fb41f651eca45dd62314609c2f389d5dc683c36e))
# [1.29.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-11)
### Features
* **api:** add access level to invitation requests ([49b3d27](https://github.com/standardnotes/app/commit/49b3d2714fbe4ac9f9242097b8df6b4e3d604513))
# [1.28.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-11)
### Features
* **api:** add accepting invites to workspace ([15e2c82](https://github.com/standardnotes/app/commit/15e2c82e654ce3f983cd42b01f14d18435b3e939))
* **api:** add listing workspaces ([8376381](https://github.com/standardnotes/app/commit/837638198112df24c30e53803a7ddf5f841975b0))
## [1.27.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-10)
### Bug Fixes
* dark mode not working in editors ([#1773](https://github.com/standardnotes/app/issues/1773)) ([27d2c95](https://github.com/standardnotes/app/commit/27d2c95b5b82645bf6c0372979f05236c83a10f6))
# [1.27.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-10)
### Bug Fixes
* **api:** workspace creation arguments ([a275a45](https://github.com/standardnotes/app/commit/a275a45753abc4a29a89de5fe784260165297bbe))
* linter issues ([e2e7605](https://github.com/standardnotes/app/commit/e2e76054142e8d67cac69280e2a3efcb15cf67f1))
### Features
* **api:** add inviting to workspace ([158ca6a](https://github.com/standardnotes/app/commit/158ca6ac6a428e082c8f984791f80fcaadcb3e34))
# [1.26.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-07)
### Features
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/services",
"version": "1.26.0",
"version": "1.32.2",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
@@ -26,7 +26,7 @@
"dependencies": {
"@standardnotes/api": "workspace:^",
"@standardnotes/auth": "^3.19.4",
"@standardnotes/common": "^1.32.0",
"@standardnotes/common": "^1.39.0",
"@standardnotes/encryption": "workspace:^",
"@standardnotes/files": "workspace:^",
"@standardnotes/models": "workspace:^",
@@ -77,7 +77,7 @@ describe('fileService', () => {
crypto.xchacha20StreamEncryptorPush = jest.fn().mockReturnValue(new Uint8Array())
})
it.only('should cache file after download', async () => {
it('should cache file after download', async () => {
const file = {
uuid: '1',
decryptedSize: 100_000,
@@ -4,7 +4,7 @@ import {
FileItem,
SNTag,
SmartView,
TagNoteCountChangeObserver,
TagItemCountChangeObserver,
DecryptedPayloadInterface,
EncryptedItemInterface,
DecryptedTransferPayload,
@@ -14,6 +14,7 @@ import {
SNTheme,
DisplayOptions,
ItemsKeyInterface,
ItemContent,
} from '@standardnotes/models'
export interface ItemsClientInterface {
@@ -23,12 +24,12 @@ export interface ItemsClientInterface {
disassociateFileWithNote(file: FileItem, note: SNNote): Promise<FileItem>
getFilesForNote(note: SNNote): FileItem[]
renameFile(file: FileItem, name: string): Promise<FileItem>
addTagToNote(note: SNNote, tag: SNTag, addHierarchy: boolean): Promise<SNTag[]>
addTagToFile(file: FileItem, tag: SNTag, addHierarchy: boolean): Promise<SNTag[]>
/** Creates an unmanaged, un-inserted item from a payload. */
createItemFromPayload(payload: DecryptedPayloadInterface): DecryptedItemInterface
@@ -54,7 +55,7 @@ export interface ItemsClientInterface {
notesMatchingSmartView(view: SmartView): SNNote[]
addNoteCountChangeObserver(observer: TagNoteCountChangeObserver): () => void
addNoteCountChangeObserver(observer: TagItemCountChangeObserver): () => void
allCountableNotesCount(): number
@@ -72,6 +73,14 @@ export interface ItemsClientInterface {
itemsReferencingItem(itemToLookupUuidFor: DecryptedItemInterface, contentType?: ContentType): DecryptedItemInterface[]
linkNoteToNote(note: SNNote, otherNote: SNNote): Promise<SNNote>
linkFileToFile(file: FileItem, otherFile: FileItem): Promise<FileItem>
unlinkItem(
item: DecryptedItemInterface<ItemContent>,
itemToUnlink: DecryptedItemInterface<ItemContent>,
): Promise<DecryptedItemInterface<ItemContent>>
/**
* Finds tags with title or component starting with a search query and (optionally) not associated with a note
* @param searchQuery - The query string to match
@@ -101,10 +110,16 @@ export interface ItemsClientInterface {
/**
* Get tags for a note sorted in natural order
* @param note - The note whose tags will be returned
* @returns Array containing tags associated with a note
* @param item - The item whose tags will be returned
* @returns Array containing tags associated with an item
*/
getSortedTagsForNote(note: SNNote): SNTag[]
getSortedTagsForItem(item: DecryptedItemInterface<ItemContent>): SNTag[]
getSortedLinkedFilesForItem(item: DecryptedItemInterface<ItemContent>): FileItem[]
getSortedFilesLinkingToItem(item: DecryptedItemInterface<ItemContent>): FileItem[]
getSortedLinkedNotesForItem(item: DecryptedItemInterface<ItemContent>): SNNote[]
getSortedNotesLinkingToItem(item: DecryptedItemInterface<ItemContent>): SNNote[]
isSmartViewTitle(title: string): boolean
@@ -137,4 +152,12 @@ export interface ItemsClientInterface {
* @returns Whether the item is a template (unmanaged)
*/
isTemplateItem(item: DecryptedItemInterface): boolean
/**
* @returns `'direct'` if `itemOne` has the reference to `itemTwo`, `'indirect'` if `itemTwo` has the reference to `itemOne`, `'unlinked'` if neither reference each other
*/
relationshipTypeForItems(
itemOne: DecryptedItemInterface,
itemTwo: DecryptedItemInterface,
): 'direct' | 'indirect' | 'unlinked'
}
@@ -1,8 +1,30 @@
import { Uuid, WorkspaceAccessLevel, WorkspaceType } from '@standardnotes/common'
import { Workspace, WorkspaceUser } from '@standardnotes/models'
export interface WorkspaceClientInterface {
createWorkspace(dto: {
encryptedWorkspaceKey: string
encryptedPrivateKey: string
publicKey: string
workspaceType: WorkspaceType
encryptedWorkspaceKey?: string
encryptedPrivateKey?: string
publicKey?: string
workspaceName?: string
}): Promise<{ uuid: string } | null>
inviteToWorkspace(dto: {
inviteeEmail: string
workspaceUuid: Uuid
accessLevel: WorkspaceAccessLevel
}): Promise<{ uuid: string } | null>
acceptInvite(dto: {
inviteUuid: Uuid
userUuid: Uuid
publicKey: string
encryptedPrivateKey: string
}): Promise<{ success: boolean }>
listWorkspaces(): Promise<{ ownedWorkspaces: Array<Workspace>; joinedWorkspaces: Array<Workspace> }>
listWorkspaceUsers(dto: { workspaceUuid: Uuid }): Promise<{ users: Array<WorkspaceUser> }>
initiateKeyshare(dto: {
workspaceUuid: Uuid
userUuid: Uuid
encryptedWorkspaceKey: string
}): Promise<{ success: boolean }>
}
@@ -1,4 +1,7 @@
import { WorkspaceApiServiceInterface } from '@standardnotes/api'
import { Uuid, WorkspaceAccessLevel, WorkspaceType } from '@standardnotes/common'
import { Workspace, WorkspaceUser } from '@standardnotes/models'
import { InternalEventBusInterface } from '../Internal/InternalEventBusInterface'
import { AbstractService } from '../Service/AbstractService'
import { WorkspaceClientInterface } from './WorkspaceClientInterface'
@@ -11,10 +14,94 @@ export class WorkspaceManager extends AbstractService implements WorkspaceClient
super(internalEventBus)
}
async createWorkspace(dto: {
async initiateKeyshare(dto: {
workspaceUuid: string
userUuid: string
encryptedWorkspaceKey: string
encryptedPrivateKey: string
}): Promise<{ success: boolean }> {
try {
const result = await this.workspaceApiService.initiateKeyshare(dto)
if (result.data.error !== undefined) {
return { success: false }
}
return result.data
} catch (error) {
return { success: false }
}
}
async listWorkspaceUsers(dto: { workspaceUuid: string }): Promise<{ users: WorkspaceUser[] }> {
try {
const result = await this.workspaceApiService.listWorkspaceUsers(dto)
if (result.data.error !== undefined) {
return { users: [] }
}
return result.data
} catch (error) {
return { users: [] }
}
}
async listWorkspaces(): Promise<{ ownedWorkspaces: Workspace[]; joinedWorkspaces: Workspace[] }> {
try {
const result = await this.workspaceApiService.listWorkspaces()
if (result.data.error !== undefined) {
return { ownedWorkspaces: [], joinedWorkspaces: [] }
}
return result.data
} catch (error) {
return { ownedWorkspaces: [], joinedWorkspaces: [] }
}
}
async acceptInvite(dto: {
inviteUuid: string
userUuid: string
publicKey: string
encryptedPrivateKey: string
}): Promise<{ success: boolean }> {
try {
const result = await this.workspaceApiService.acceptInvite(dto)
if (result.data.error !== undefined) {
return { success: false }
}
return result.data
} catch (error) {
return { success: false }
}
}
async inviteToWorkspace(dto: {
inviteeEmail: string
workspaceUuid: Uuid
accessLevel: WorkspaceAccessLevel
}): Promise<{ uuid: string } | null> {
try {
const result = await this.workspaceApiService.inviteToWorkspace(dto)
if (result.data.error !== undefined) {
return null
}
return result.data
} catch (error) {
return null
}
}
async createWorkspace(dto: {
workspaceType: WorkspaceType
encryptedWorkspaceKey?: string
encryptedPrivateKey?: string
publicKey?: string
workspaceName?: string
}): Promise<{ uuid: string } | null> {
try {
+46
View File
@@ -3,6 +3,52 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [2.139.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-12)
### Features
* replace private workspaces with private usernames ([#1783](https://github.com/standardnotes/app/issues/1783)) ([18c821d](https://github.com/standardnotes/app/commit/18c821d8eb51beb6f54211eb1d6eb454303044f5))
## [2.138.2](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-12)
**Note:** Version bump only for package @standardnotes/snjs
## [2.138.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-12)
**Note:** Version bump only for package @standardnotes/snjs
# [2.138.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-11)
### Features
* item linking ([#1779](https://github.com/standardnotes/app/issues/1779)) ([e3f2842](https://github.com/standardnotes/app/commit/e3f28421ff042c635ad2ae645c102c27e3e3f9c7))
# [2.137.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-11)
### Features
* **api:** add listing workspace users ([fb41f65](https://github.com/standardnotes/app/commit/fb41f651eca45dd62314609c2f389d5dc683c36e))
## [2.136.4](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-11)
**Note:** Version bump only for package @standardnotes/snjs
## [2.136.3](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-11)
**Note:** Version bump only for package @standardnotes/snjs
## [2.136.2](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-10)
### Bug Fixes
* dark mode not working in editors ([#1773](https://github.com/standardnotes/app/issues/1773)) ([27d2c95](https://github.com/standardnotes/app/commit/27d2c95b5b82645bf6c0372979f05236c83a10f6))
## [2.136.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-10)
### Bug Fixes
* **api:** workspace creation arguments ([a275a45](https://github.com/standardnotes/app/commit/a275a45753abc4a29a89de5fe784260165297bbe))
# [2.136.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-07)
### Features
+6 -4
View File
@@ -53,7 +53,7 @@ import {
WorkspaceManager,
} from '@standardnotes/services'
import { FilesClientInterface } from '@standardnotes/files'
import { ComputePrivateWorkspaceIdentifier } from '@standardnotes/encryption'
import { ComputePrivateUsername } from '@standardnotes/encryption'
import { useBoolean } from '@standardnotes/utils'
import {
BackupFile,
@@ -272,8 +272,8 @@ export class SNApplication
return this.componentManagerService
}
public computePrivateWorkspaceIdentifier(userphrase: string, name: string): Promise<string | undefined> {
return ComputePrivateWorkspaceIdentifier(this.options.crypto, userphrase, name)
public computePrivateUsername(username: string): Promise<string | undefined> {
return ComputePrivateUsername(this.options.crypto, username)
}
/**
@@ -1497,7 +1497,10 @@ export class SNApplication
const syncEventCallback = async (eventName: ExternalServices.SyncEvent) => {
const appEvent = applicationEventForSyncEvent(eventName)
if (appEvent) {
await this.protocolService.onSyncEvent(eventName)
await this.notifyEvent(appEvent)
if (appEvent === ApplicationEvent.CompletedFullSync) {
if (!this.handledFullSyncStage) {
this.handledFullSyncStage = true
@@ -1505,7 +1508,6 @@ export class SNApplication
}
}
}
await this.protocolService.onSyncEvent(eventName)
}
const uninstall = this.syncService.addEventObserver(syncEventCallback)
this.serviceObservers.push(uninstall)
@@ -44,6 +44,7 @@ import {
SetOfflineFeaturesFunctionResponse,
StorageKey,
} from '@standardnotes/services'
import { FeatureIdentifier } from '@standardnotes/features'
type GetOfflineSubscriptionDetailsResponse = OfflineSubscriptionEntitlements | ClientDisplayableError
@@ -145,7 +146,10 @@ export class SNFeaturesService
override async handleApplicationStage(stage: ApplicationStage): Promise<void> {
await super.handleApplicationStage(stage)
if (stage === ApplicationStage.FullSyncCompleted_13) {
void this.addDarkTheme()
if (!this.hasOnlineSubscription()) {
const offlineRepo = this.getOfflineRepo()
if (offlineRepo) {
@@ -155,6 +159,14 @@ export class SNFeaturesService
}
}
private async addDarkTheme() {
const darkThemeFeature = FeaturesImports.FindNativeFeature(FeatureIdentifier.DarkTheme)
if (darkThemeFeature) {
await this.mapRemoteNativeFeaturesToItems([darkThemeFeature])
}
}
public enableExperimentalFeature(identifier: FeaturesImports.FeatureIdentifier): void {
const feature = this.getUserFeature(identifier)
if (!feature) {
@@ -366,7 +378,7 @@ export class SNFeaturesService
if (!arraysEqual(this.roles, roles)) {
void this.notifyEvent(FeaturesEvent.UserRolesChanged)
}
await this.storageService.setValue(StorageKey.UserRoles, this.roles)
this.storageService.setValue(StorageKey.UserRoles, this.roles)
}
public async didDownloadFeatures(features: FeaturesImports.FeatureDescription[]): Promise<void> {
@@ -448,7 +460,15 @@ export class SNFeaturesService
return FeaturesImports.FindNativeFeature(featureId)?.deprecated === true
}
public isFreeFeature(featureId: FeaturesImports.FeatureIdentifier) {
return [FeatureIdentifier.DarkTheme].includes(featureId)
}
public getFeatureStatus(featureId: FeaturesImports.FeatureIdentifier): FeatureStatus {
if (this.isFreeFeature(featureId)) {
return FeatureStatus.Entitled
}
const isDeprecated = this.isFeatureDeprecated(featureId)
if (isDeprecated) {
if (this.hasPaidOnlineOrOfflineSubscription()) {
@@ -548,7 +568,9 @@ export class SNFeaturesService
let hasChanges = false
const now = new Date()
const expired = new Date(feature.expires_at || 0).getTime() < now.getTime()
const expired = this.isFreeFeature(feature.identifier)
? false
: new Date(feature.expires_at || 0).getTime() < now.getTime()
const existingItem = currentItems.find((item) => {
if (item.content.package_info) {
@@ -405,41 +405,6 @@ describe('itemManager', () => {
const notes = itemManager.getDisplayableNotes()
expect(notes).toHaveLength(1)
})
it('adding a note to a tag hierarchy should add the note to its parent too', async () => {
itemManager = createService()
const parentTag = createTag('parent')
const childTag = createTag('child')
const note = createNote('note')
await itemManager.insertItems([parentTag, childTag, note])
await itemManager.setTagParent(parentTag, childTag)
await itemManager.addTagToNote(note, childTag, true)
const tags = itemManager.getSortedTagsForNote(note)
expect(tags).toHaveLength(2)
expect(tags[0].uuid).toEqual(childTag.uuid)
expect(tags[1].uuid).toEqual(parentTag.uuid)
})
it('adding a note to a tag hierarchy should not add the note to its parent if hierarchy option is disabled', async () => {
itemManager = createService()
const parentTag = createTag('parent')
const childTag = createTag('child')
const note = createNote('note')
await itemManager.insertItems([parentTag, childTag, note])
await itemManager.setTagParent(parentTag, childTag)
await itemManager.addTagToNote(note, childTag, false)
const tags = itemManager.getSortedTagsForNote(note)
expect(tags).toHaveLength(1)
expect(tags[0].uuid).toEqual(childTag.uuid)
})
})
describe('template items', () => {
@@ -703,47 +668,6 @@ describe('itemManager', () => {
})
describe('files', () => {
it('associates with note', async () => {
itemManager = createService()
const note = createNote('invoices')
const file = createFile('invoice_1.pdf')
await itemManager.insertItems([note, file])
const resultingFile = await itemManager.associateFileWithNote(file, note)
const references = resultingFile.references
expect(references).toHaveLength(1)
expect(references[0].uuid).toEqual(note.uuid)
})
it('disassociates with note', async () => {
itemManager = createService()
const note = createNote('invoices')
const file = createFile('invoice_1.pdf')
await itemManager.insertItems([note, file])
const associatedFile = await itemManager.associateFileWithNote(file, note)
const disassociatedFile = await itemManager.disassociateFileWithNote(associatedFile, note)
const references = disassociatedFile.references
expect(references).toHaveLength(0)
})
it('should get files associated with note', async () => {
itemManager = createService()
const note = createNote('invoices')
const file = createFile('invoice_1.pdf')
const secondFile = createFile('unrelated-file.xlsx')
await itemManager.insertItems([note, file, secondFile])
await itemManager.associateFileWithNote(file, note)
const filesAssociatedWithNote = itemManager.getFilesForNote(note)
expect(filesAssociatedWithNote).toHaveLength(1)
expect(filesAssociatedWithNote[0].uuid).toBe(file.uuid)
})
it('should correctly rename file to filename that has extension', async () => {
itemManager = createService()
const file = createFile('initialName.ext')
@@ -774,4 +698,249 @@ describe('itemManager', () => {
expect(renamedFile.name).toBe('anotherName')
})
})
describe('linking', () => {
it('adding a note to a tag hierarchy should add the note to its parent too', async () => {
itemManager = createService()
const parentTag = createTag('parent')
const childTag = createTag('child')
const note = createNote('note')
await itemManager.insertItems([parentTag, childTag, note])
await itemManager.setTagParent(parentTag, childTag)
await itemManager.addTagToNote(note, childTag, true)
const tags = itemManager.getSortedTagsForItem(note)
expect(tags).toHaveLength(2)
expect(tags[0].uuid).toEqual(childTag.uuid)
expect(tags[1].uuid).toEqual(parentTag.uuid)
})
it('adding a note to a tag hierarchy should not add the note to its parent if hierarchy option is disabled', async () => {
itemManager = createService()
const parentTag = createTag('parent')
const childTag = createTag('child')
const note = createNote('note')
await itemManager.insertItems([parentTag, childTag, note])
await itemManager.setTagParent(parentTag, childTag)
await itemManager.addTagToNote(note, childTag, false)
const tags = itemManager.getSortedTagsForItem(note)
expect(tags).toHaveLength(1)
expect(tags[0].uuid).toEqual(childTag.uuid)
})
it('adding a file to a tag hierarchy should add the file to its parent too', async () => {
itemManager = createService()
const parentTag = createTag('parent')
const childTag = createTag('child')
const file = createFile('file')
await itemManager.insertItems([parentTag, childTag, file])
await itemManager.setTagParent(parentTag, childTag)
await itemManager.addTagToFile(file, childTag, true)
const tags = itemManager.getSortedTagsForItem(file)
expect(tags).toHaveLength(2)
expect(tags[0].uuid).toEqual(childTag.uuid)
expect(tags[1].uuid).toEqual(parentTag.uuid)
})
it('adding a file to a tag hierarchy should not add the file to its parent if hierarchy option is disabled', async () => {
itemManager = createService()
const parentTag = createTag('parent')
const childTag = createTag('child')
const file = createFile('file')
await itemManager.insertItems([parentTag, childTag, file])
await itemManager.setTagParent(parentTag, childTag)
await itemManager.addTagToFile(file, childTag, false)
const tags = itemManager.getSortedTagsForItem(file)
expect(tags).toHaveLength(1)
expect(tags[0].uuid).toEqual(childTag.uuid)
})
it('should link file with note', async () => {
itemManager = createService()
const note = createNote('invoices')
const file = createFile('invoice_1.pdf')
await itemManager.insertItems([note, file])
const resultingFile = await itemManager.associateFileWithNote(file, note)
const references = resultingFile.references
expect(references).toHaveLength(1)
expect(references[0].uuid).toEqual(note.uuid)
})
it('should unlink file from note', async () => {
itemManager = createService()
const note = createNote('invoices')
const file = createFile('invoice_1.pdf')
await itemManager.insertItems([note, file])
const associatedFile = await itemManager.associateFileWithNote(file, note)
const disassociatedFile = await itemManager.disassociateFileWithNote(associatedFile, note)
const references = disassociatedFile.references
expect(references).toHaveLength(0)
})
it('should get files linked with note', async () => {
itemManager = createService()
const note = createNote('invoices')
const file = createFile('invoice_1.pdf')
const secondFile = createFile('unrelated-file.xlsx')
await itemManager.insertItems([note, file, secondFile])
await itemManager.associateFileWithNote(file, note)
const filesAssociatedWithNote = itemManager.getSortedFilesLinkingToItem(note)
expect(filesAssociatedWithNote).toHaveLength(1)
expect(filesAssociatedWithNote[0].uuid).toBe(file.uuid)
})
it('should link note to note', async () => {
itemManager = createService()
const note = createNote('research')
const note2 = createNote('citation')
await itemManager.insertItems([note, note2])
const resultingNote = await itemManager.linkNoteToNote(note, note2)
const references = resultingNote.references
expect(references).toHaveLength(1)
expect(references[0].uuid).toEqual(note2.uuid)
})
it('should link file to file', async () => {
itemManager = createService()
const file = createFile('research')
const file2 = createFile('citation')
await itemManager.insertItems([file, file2])
const resultingfile = await itemManager.linkFileToFile(file, file2)
const references = resultingfile.references
expect(references).toHaveLength(1)
expect(references[0].uuid).toEqual(file2.uuid)
})
it('should get the relationship type for two items', async () => {
itemManager = createService()
const firstNote = createNote('First note')
const secondNote = createNote('Second note')
const unlinkedNote = createNote('Unlinked note')
await itemManager.insertItems([firstNote, secondNote, unlinkedNote])
const firstNoteLinkedToSecond = await itemManager.linkNoteToNote(firstNote, secondNote)
const relationshipOfFirstNoteToSecond = itemManager.relationshipTypeForItems(firstNoteLinkedToSecond, secondNote)
const relationshipOfSecondNoteToFirst = itemManager.relationshipTypeForItems(secondNote, firstNoteLinkedToSecond)
const relationshipOfFirstNoteToUnlinked = itemManager.relationshipTypeForItems(
firstNoteLinkedToSecond,
unlinkedNote,
)
expect(relationshipOfFirstNoteToSecond).toBe('direct')
expect(relationshipOfSecondNoteToFirst).toBe('indirect')
expect(relationshipOfFirstNoteToUnlinked).toBe('unlinked')
})
it('should unlink itemToUnlink from item', async () => {
itemManager = createService()
const note = createNote('Note 1')
const note2 = createNote('Note 2')
await itemManager.insertItems([note, note2])
const linkedItem = await itemManager.linkNoteToNote(note, note2)
const unlinkedItem = await itemManager.unlinkItem(linkedItem, note2)
const references = unlinkedItem.references
expect(references).toHaveLength(0)
})
it('should get all linked files for item', async () => {
itemManager = createService()
const file = createFile('A1')
const file2 = createFile('B2')
const file3 = createFile('C3')
await itemManager.insertItems([file, file2, file3])
await itemManager.linkFileToFile(file, file3)
await itemManager.linkFileToFile(file, file2)
const sortedFilesForItem = itemManager.getSortedLinkedFilesForItem(file)
expect(sortedFilesForItem).toHaveLength(2)
expect(sortedFilesForItem[0].uuid).toEqual(file2.uuid)
expect(sortedFilesForItem[1].uuid).toEqual(file3.uuid)
})
it('should get all files linking to item', async () => {
itemManager = createService()
const baseFile = createFile('file')
const fileToLink1 = createFile('A1')
const fileToLink2 = createFile('B2')
await itemManager.insertItems([baseFile, fileToLink1, fileToLink2])
await itemManager.linkFileToFile(fileToLink2, baseFile)
await itemManager.linkFileToFile(fileToLink1, baseFile)
const sortedFilesForItem = itemManager.getSortedFilesLinkingToItem(baseFile)
expect(sortedFilesForItem).toHaveLength(2)
expect(sortedFilesForItem[0].uuid).toEqual(fileToLink1.uuid)
expect(sortedFilesForItem[1].uuid).toEqual(fileToLink2.uuid)
})
it('should get all linked notes for item', async () => {
itemManager = createService()
const baseNote = createNote('note')
const noteToLink1 = createNote('A1')
const noteToLink2 = createNote('B2')
await itemManager.insertItems([baseNote, noteToLink1, noteToLink2])
await itemManager.linkNoteToNote(baseNote, noteToLink2)
await itemManager.linkNoteToNote(baseNote, noteToLink1)
const sortedFilesForItem = itemManager.getSortedLinkedNotesForItem(baseNote)
expect(sortedFilesForItem).toHaveLength(2)
expect(sortedFilesForItem[0].uuid).toEqual(noteToLink1.uuid)
expect(sortedFilesForItem[1].uuid).toEqual(noteToLink2.uuid)
})
it('should get all notes linking to item', async () => {
itemManager = createService()
const baseNote = createNote('note')
const noteToLink1 = createNote('A1')
const noteToLink2 = createNote('B2')
await itemManager.insertItems([baseNote, noteToLink1, noteToLink2])
await itemManager.linkNoteToNote(noteToLink2, baseNote)
await itemManager.linkNoteToNote(noteToLink1, baseNote)
const sortedFilesForItem = itemManager.getSortedNotesLinkingToItem(baseNote)
expect(sortedFilesForItem).toHaveLength(2)
expect(sortedFilesForItem[0].uuid).toEqual(noteToLink1.uuid)
expect(sortedFilesForItem[1].uuid).toEqual(noteToLink2.uuid)
})
})
})
+118 -21
View File
@@ -9,7 +9,7 @@ import * as Services from '@standardnotes/services'
import { PayloadManagerChangeData } from '../Payloads'
import { DiagnosticInfo, ItemsClientInterface } from '@standardnotes/services'
import { ApplicationDisplayOptions } from '@Lib/Application/Options/OptionalOptions'
import { CollectionSort } from '@standardnotes/models'
import { CollectionSort, DecryptedItemInterface, ItemContent } from '@standardnotes/models'
type ItemsChangeObserver<I extends Models.DecryptedItemInterface = Models.DecryptedItemInterface> = {
contentType: ContentType[]
@@ -32,7 +32,7 @@ export class ItemManager
private observers: ItemsChangeObserver[] = []
private collection!: Models.ItemCollection
private systemSmartViews: Models.SmartView[]
private tagNotesIndex!: Models.TagNotesIndex
private tagItemsIndex!: Models.TagItemsIndex
private navigationDisplayController!: Models.ItemDisplayController<Models.SNNote | Models.FileItem>
private tagDisplayController!: Models.ItemDisplayController<Models.SNTag>
@@ -96,7 +96,7 @@ export class ItemManager
sortDirection: 'asc',
})
this.tagNotesIndex = new Models.TagNotesIndex(this.collection, this.tagNotesIndex?.observers)
this.tagItemsIndex = new Models.TagItemsIndex(this.collection, this.tagItemsIndex?.observers)
}
private get allDisplayControllers(): Models.ItemDisplayController<Models.DisplayItem>[] {
@@ -219,7 +219,7 @@ export class ItemManager
;(this.unsubChangeObserver as unknown) = undefined
;(this.payloadManager as unknown) = undefined
;(this.collection as unknown) = undefined
;(this.tagNotesIndex as unknown) = undefined
;(this.tagItemsIndex as unknown) = undefined
;(this.tagDisplayController as unknown) = undefined
;(this.navigationDisplayController as unknown) = undefined
;(this.itemsKeyDisplayController as unknown) = undefined
@@ -284,23 +284,23 @@ export class ItemManager
return TagsToFoldersMigrationApplicator.isApplicableToCurrentData(this)
}
public addNoteCountChangeObserver(observer: Models.TagNoteCountChangeObserver): () => void {
return this.tagNotesIndex.addCountChangeObserver(observer)
public addNoteCountChangeObserver(observer: Models.TagItemCountChangeObserver): () => void {
return this.tagItemsIndex.addCountChangeObserver(observer)
}
public allCountableNotesCount(): number {
return this.tagNotesIndex.allCountableNotesCount()
return this.tagItemsIndex.allCountableItemsCount()
}
public countableNotesForTag(tag: Models.SNTag | Models.SmartView): number {
if (tag instanceof Models.SmartView) {
if (tag.uuid === Models.SystemViewId.AllNotes) {
return this.tagNotesIndex.allCountableNotesCount()
return this.tagItemsIndex.allCountableItemsCount()
}
throw Error('countableNotesForTag is not meant to be used for smart views.')
throw Error('countableItemsForTag is not meant to be used for smart views.')
}
return this.tagNotesIndex.countableNotesForTag(tag)
return this.tagItemsIndex.countableItemsForTag(tag)
}
public getNoteCount(): number {
@@ -406,7 +406,7 @@ export class ItemManager
}
this.collection.onChange(delta)
this.tagNotesIndex.onChange(delta)
this.tagItemsIndex.onChange(delta)
const affectedContentTypesArray = Array.from(affectedContentTypes.values())
for (const controller of this.allDisplayControllers) {
@@ -1140,20 +1140,106 @@ export class ItemManager
)
}
public async addTagToFile(file: Models.FileItem, tag: Models.SNTag, addHierarchy: boolean): Promise<Models.SNTag[]> {
let tagsToAdd = [tag]
if (addHierarchy) {
const parentChainTags = this.getTagParentChain(tag)
tagsToAdd = [...parentChainTags, tag]
}
return Promise.all(
tagsToAdd.map((tagToAdd) => {
return this.changeTag(tagToAdd, (mutator) => {
mutator.addFile(file)
}) as Promise<Models.SNTag>
}),
)
}
public async linkNoteToNote(note: Models.SNNote, otherNote: Models.SNNote): Promise<Models.SNNote> {
return this.changeItem<Models.NoteMutator, Models.SNNote>(note, (mutator) => {
mutator.addNote(otherNote)
})
}
public async linkFileToFile(file: Models.FileItem, otherFile: Models.FileItem): Promise<Models.FileItem> {
return this.changeItem<Models.FileMutator, Models.FileItem>(file, (mutator) => {
mutator.addFile(otherFile)
})
}
public async unlinkItem(
item: DecryptedItemInterface<ItemContent>,
itemToUnlink: DecryptedItemInterface<ItemContent>,
) {
return this.changeItem(item, (mutator) => {
mutator.removeItemAsRelationship(itemToUnlink)
})
}
/**
* Get tags for a note sorted in natural order
* @param note - The note whose tags will be returned
* @returns Array containing tags associated with a note
* @param item - The item whose tags will be returned
* @returns Array containing tags associated with an item
*/
public getSortedTagsForNote(note: Models.SNNote): Models.SNTag[] {
public getSortedTagsForItem(item: DecryptedItemInterface<ItemContent>): Models.SNTag[] {
return naturalSort(
this.itemsReferencingItem(note).filter((ref) => {
this.itemsReferencingItem(item).filter((ref) => {
return ref?.content_type === ContentType.Tag
}) as Models.SNTag[],
'title',
)
}
public getSortedLinkedFilesForItem(item: DecryptedItemInterface<ItemContent>): Models.FileItem[] {
if (this.isTemplateItem(item)) {
return []
}
const filesReferencedByItem = this.referencesForItem(item).filter(
(ref) => ref.content_type === ContentType.File,
) as Models.FileItem[]
return naturalSort(filesReferencedByItem, 'title')
}
public getSortedFilesLinkingToItem(item: DecryptedItemInterface<ItemContent>): Models.FileItem[] {
if (this.isTemplateItem(item)) {
return []
}
const filesReferencingItem = this.itemsReferencingItem(item).filter(
(ref) => ref.content_type === ContentType.File,
) as Models.FileItem[]
return naturalSort(filesReferencingItem, 'title')
}
public getSortedLinkedNotesForItem(item: DecryptedItemInterface<ItemContent>): Models.SNNote[] {
if (this.isTemplateItem(item)) {
return []
}
const notesReferencedByItem = this.referencesForItem(item).filter(
(ref) => ref.content_type === ContentType.Note,
) as Models.SNNote[]
return naturalSort(notesReferencedByItem, 'title')
}
public getSortedNotesLinkingToItem(item: Models.DecryptedItemInterface<Models.ItemContent>): Models.SNNote[] {
if (this.isTemplateItem(item)) {
return []
}
const notesReferencingItem = this.itemsReferencingItem(item).filter(
(ref) => ref.content_type === ContentType.Note,
) as Models.SNNote[]
return naturalSort(notesReferencingItem, 'title')
}
public async createTag(title: string, parentItemToLookupUuidFor?: Models.SNTag): Promise<Models.SNTag> {
const newTag = await this.createItem<Models.SNTag>(
ContentType.Tag,
@@ -1312,12 +1398,6 @@ export class ItemManager
}
}
public getFilesForNote(note: Models.SNNote): Models.FileItem[] {
return (
this.itemsReferencingItem(note).filter((ref) => ref.content_type === ContentType.File) as Models.FileItem[]
).sort((a, b) => (a.name.toLowerCase() > b.name.toLowerCase() ? 1 : -1))
}
public renameFile(file: Models.FileItem, name: string): Promise<Models.FileItem> {
return this.changeItem<Models.FileMutator, Models.FileItem>(file, (mutator) => {
mutator.name = name
@@ -1353,6 +1433,23 @@ export class ItemManager
return this.findAnyItems(uuids) as (Models.DecryptedItemInterface | Models.DeletedItemInterface)[]
}
/**
* @returns `'direct'` if `itemOne` has the reference to `itemTwo`, `'indirect'` if `itemTwo` has the reference to `itemOne`, `'unlinked'` if neither reference each other
*/
public relationshipTypeForItems(
itemOne: Models.DecryptedItemInterface<Models.ItemContent>,
itemTwo: Models.DecryptedItemInterface<Models.ItemContent>,
): 'direct' | 'indirect' | 'unlinked' {
const itemOneReferencesItemTwo = !!this.referencesForItem(itemOne).find(
(reference) => reference.uuid === itemTwo.uuid,
)
const itemTwoReferencesItemOne = !!this.referencesForItem(itemTwo).find(
(reference) => reference.uuid === itemOne.uuid,
)
return itemOneReferencesItemTwo ? 'direct' : itemTwoReferencesItemOne ? 'indirect' : 'unlinked'
}
override getDiagnostics(): Promise<DiagnosticInfo | undefined> {
return Promise.resolve({
items: {
+7 -8
View File
@@ -1,12 +1,11 @@
/* eslint-disable no-unused-expressions */
/* eslint-disable no-undef */
import { BaseItemCounts } from './lib/Applications.js'
import * as Factory from './lib/factory.js'
chai.use(chaiAsPromised)
const expect = chai.expect
describe('application instances', () => {
const BASE_ITEM_COUNT = 2 /** Default items key, user preferences */
const syncOptions = {
checkIntegrity: true,
awaitAll: true,
@@ -27,8 +26,8 @@ describe('application instances', () => {
expect(app1.payloadManager).to.not.equal(app2.payloadManager)
await Factory.createMappedNote(app1)
expect(app1.itemManager.items.length).length.to.equal(BASE_ITEM_COUNT + 1)
expect(app2.itemManager.items.length).to.equal(BASE_ITEM_COUNT)
expect(app1.itemManager.items.length).length.to.equal(BaseItemCounts.DefaultItems + 1)
expect(app2.itemManager.items.length).to.equal(BaseItemCounts.DefaultItems)
await Factory.safeDeinit(app1)
await Factory.safeDeinit(app2)
})
@@ -40,14 +39,14 @@ describe('application instances', () => {
await Factory.createMappedNote(app1)
await app1.syncService.sync(syncOptions)
expect((await app1.diskStorageService.getAllRawPayloads()).length).length.to.equal(BASE_ITEM_COUNT + 1)
expect((await app2.diskStorageService.getAllRawPayloads()).length).length.to.equal(BASE_ITEM_COUNT)
expect((await app1.diskStorageService.getAllRawPayloads()).length).length.to.equal(BaseItemCounts.DefaultItems + 1)
expect((await app2.diskStorageService.getAllRawPayloads()).length).length.to.equal(BaseItemCounts.DefaultItems)
await Factory.createMappedNote(app2)
await app2.syncService.sync(syncOptions)
expect((await app1.diskStorageService.getAllRawPayloads()).length).length.to.equal(BASE_ITEM_COUNT + 1)
expect((await app2.diskStorageService.getAllRawPayloads()).length).length.to.equal(BASE_ITEM_COUNT + 1)
expect((await app1.diskStorageService.getAllRawPayloads()).length).length.to.equal(BaseItemCounts.DefaultItems + 1)
expect((await app2.diskStorageService.getAllRawPayloads()).length).length.to.equal(BaseItemCounts.DefaultItems + 1)
await Factory.safeDeinit(app1)
await Factory.safeDeinit(app2)
})
@@ -1,16 +1,15 @@
/* eslint-disable no-unused-expressions */
/* eslint-disable no-undef */
import { BaseItemCounts } from './lib/Applications.js'
import * as Factory from './lib/factory.js'
chai.use(chaiAsPromised)
const expect = chai.expect
describe('auth fringe cases', () => {
const BASE_ITEM_COUNT = ['default items key', 'user prefs'].length
const createContext = async () => {
const application = await Factory.createInitAppWithFakeCrypto()
return {
expectedItemCount: BASE_ITEM_COUNT,
expectedItemCount: BaseItemCounts.DefaultItems,
application: application,
email: UuidGenerator.GenerateUuid(),
password: UuidGenerator.GenerateUuid(),
@@ -100,9 +99,13 @@ describe('auth fringe cases', () => {
expect(newApplication.itemManager.getDisplayableNotes().length).to.equal(2)
expect(newApplication.itemManager.getDisplayableNotes().find((n) => n.uuid === firstVersionOfNote.uuid).text).to.equal(staleText)
expect(
newApplication.itemManager.getDisplayableNotes().find((n) => n.uuid === firstVersionOfNote.uuid).text,
).to.equal(staleText)
const conflictedCopy = newApplication.itemManager.getDisplayableNotes().find((n) => n.uuid !== firstVersionOfNote.uuid)
const conflictedCopy = newApplication.itemManager
.getDisplayableNotes()
.find((n) => n.uuid !== firstVersionOfNote.uuid)
expect(conflictedCopy.text).to.equal(serverText)
expect(conflictedCopy.duplicate_of).to.equal(firstVersionOfNote.uuid)
await Factory.safeDeinit(newApplication)
+4 -5
View File
@@ -1,5 +1,6 @@
/* eslint-disable no-unused-expressions */
/* eslint-disable no-undef */
import { BaseItemCounts } from './lib/Applications.js'
import * as Factory from './lib/factory.js'
chai.use(chaiAsPromised)
const expect = chai.expect
@@ -7,8 +8,6 @@ const expect = chai.expect
describe('basic auth', function () {
this.timeout(Factory.TenSecondTimeout)
const BASE_ITEM_COUNT = 2 /** Default items key, user preferences */
const syncOptions = {
checkIntegrity: true,
awaitAll: true,
@@ -16,7 +15,7 @@ describe('basic auth', function () {
beforeEach(async function () {
localStorage.clear()
this.expectedItemCount = BASE_ITEM_COUNT
this.expectedItemCount = BaseItemCounts.DefaultItems
this.application = await Factory.createInitAppWithFakeCrypto()
this.email = UuidGenerator.GenerateUuid()
this.password = UuidGenerator.GenerateUuid()
@@ -62,7 +61,7 @@ describe('basic auth', function () {
expect(this.application.protocolService.rootKeyEncryption.keyMode).to.equal(KeyMode.RootKeyNone)
const rawPayloads = await this.application.diskStorageService.getAllRawPayloads()
expect(rawPayloads.length).to.equal(BASE_ITEM_COUNT)
expect(rawPayloads.length).to.equal(BaseItemCounts.DefaultItems)
})
it('successfully signs in to registered account', async function () {
@@ -408,7 +407,7 @@ describe('basic auth', function () {
this.application = await Factory.signOutApplicationAndReturnNew(this.application)
expect(this.application.itemManager.items.length).to.equal(BASE_ITEM_COUNT)
expect(this.application.itemManager.items.length).to.equal(BaseItemCounts.DefaultItems)
expect(this.application.payloadManager.invalidPayloads.length).to.equal(0)
/** Should login with new password */
+3 -2
View File
@@ -1,5 +1,6 @@
/* eslint-disable no-unused-expressions */
/* eslint-disable no-undef */
import { BaseItemCounts } from './lib/Applications.js'
import * as Factory from './lib/factory.js'
chai.use(chaiAsPromised)
const expect = chai.expect
@@ -24,8 +25,8 @@ describe('backups', function () {
this.application = null
})
const BASE_ITEM_COUNT_ENCRYPTED = ['ItemsKey', 'UserPreferences'].length
const BASE_ITEM_COUNT_DECRYPTED = ['UserPreferences'].length
const BASE_ITEM_COUNT_ENCRYPTED = BaseItemCounts.DefaultItems
const BASE_ITEM_COUNT_DECRYPTED = ['UserPreferences', 'DarkTheme'].length
it('backup file should have a version number', async function () {
let data = await this.application.createDecryptedBackupFile()

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